<commit_before>
package com.nasageek.utexasutilities.activities;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnTouchListener;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;

import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.Window;
import com.nasageek.utexasutilities.AsyncTask;
import com.nasageek.utexasutilities.AuthCookie;
import com.nasageek.utexasutilities.ChangeLog;
import com.nasageek.utexasutilities.ChangeableContextTask;
import com.nasageek.utexasutilities.R;
import com.nasageek.utexasutilities.SecurePreferences;
import com.nasageek.utexasutilities.UTilitiesApplication;
import com.nasageek.utexasutilities.Utility;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import static com.nasageek.utexasutilities.UTilitiesApplication.BB_AUTH_COOKIE_KEY;
import static com.nasageek.utexasutilities.UTilitiesApplication.PNA_AUTH_COOKIE_KEY;
import static com.nasageek.utexasutilities.UTilitiesApplication.UTD_AUTH_COOKIE_KEY;

/**
 * Main entry point for UTilities. Allows the user to log in and contains a dashboard
 * of buttons to launch UT web services.
 */
public class UTilitiesActivity extends BaseActivity {

    private final static int BUTTON_ANIMATION_DURATION = 90;

    private final static String SECURE_PREF_PW_KEY = "<PASSWORD>";

    private SharedPreferences settings;
    private Toast message;
    private ImageView scheduleCheck, balanceCheck, dataCheck, blackboardCheck;
    private AlertDialog nologin;

    private AuthCookie authCookies[];
    private List<AsyncTask> loginTasks;
    private UpdateUiTask updateUiTask;
    private AuthCookie utdAuthCookie;
    private AuthCookie pnaAuthCookie;
    private AuthCookie bbAuthCookie;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        UTilitiesApplication mApp = (UTilitiesApplication) getApplication();
        utdAuthCookie = mApp.getAuthCookie(UTD_AUTH_COOKIE_KEY);
        pnaAuthCookie = mApp.getAuthCookie(PNA_AUTH_COOKIE_KEY);
        bbAuthCookie = mApp.getAuthCookie(BB_AUTH_COOKIE_KEY);
        authCookies = new AuthCookie[]{utdAuthCookie, pnaAuthCookie, bbAuthCookie};

        loginTasks = (List<AsyncTask>) getLastCustomNonConfigurationInstance();
        if (loginTasks != null) {
            updateUiTask = (UpdateUiTask) loginTasks.get(0);
            if (updateUiTask != null) {
                updateUiTask.setContext(this);
            }
        }

        settings = PreferenceManager.getDefaultSharedPreferences(this.getBaseContext());
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.main);
        setSupportProgressBarIndeterminateVisibility(isLoggingIn());

        // use one Activity-wide Toast so they don't stack up
        message = Toast.makeText(this, R.string.login_first, Toast.LENGTH_SHORT);

        handleUnencryptedPassword();
        handleFirstLaunch();
        if (settings.getBoolean("autologin", false) && !isLoggingIn() && !mApp.allCookiesSet()) {
            login();
        }
        setupDashBoardButtons();
    }

    /**
     * Set up the dashboard of buttons to launch the various services of the
     * app. This covers things like touch/focus listeners, authentication,
     * intent to launch, etc.
     */
    private void setupDashBoardButtons() {
        scheduleCheck = (ImageView) findViewById(R.id.scheduleCheck);
        balanceCheck = (ImageView) findViewById(R.id.balanceCheck);
        dataCheck = (ImageView) findViewById(R.id.dataCheck);
        blackboardCheck = (ImageView) findViewById(R.id.blackboardCheck);

        final Intent schedule = new Intent(this, ScheduleActivity.class);
        final Intent balance = new Intent(this, BalanceActivity.class);
        final Intent map = new Intent(this, CampusMapActivity.class);
        final Intent data = new Intent(this, DataUsageActivity.class);
        final Intent menu = new Intent(this, MenuActivity.class);
        final Intent blackboard = new Intent(this, BlackboardPanesActivity.class);

        // simple struct-like class to help handle related data
        class DashboardButtonData {
            public Intent intent;
            public int imageButtonId;
            public AuthCookie authCookie;
            public Character serviceChar;
            public ImageView checkOverlay;

            // for authenticated services
            public DashboardButtonData(Intent intent, int id, AuthCookie authCookie,
                                       Character service, ImageView check) {
                this.intent = intent;
                this.imageButtonId = id;
                this.authCookie = authCookie;
                this.serviceChar = service;
                this.checkOverlay = check;
            }

            // for unauthenticated services
            public DashboardButtonData(Intent intent, int id) {
                this(intent, id, null, null, null);
            }
        }

        DashboardButtonData buttonData[] = new DashboardButtonData[6];
        buttonData[0] = new DashboardButtonData(schedule, R.id.schedule_button, utdAuthCookie, 'u',
                scheduleCheck);
        buttonData[1] = new DashboardButtonData(balance, R.id.balance_button, utdAuthCookie, 'u',
                balanceCheck);
        buttonData[2] = new DashboardButtonData(blackboard, R.id.blackboard_button, bbAuthCookie,
                'b', blackboardCheck);
        buttonData[3] = new DashboardButtonData(data, R.id.data_button, pnaAuthCookie, 'p',
                dataCheck);
        buttonData[4] = new DashboardButtonData(map, R.id.map_button);
        buttonData[5] = new DashboardButtonData(menu, R.id.menu_button);

        for (int i = 0; i < 6; i++) {
            ImageButton ib = (ImageButton) findViewById(buttonData[i].imageButtonId);
            ib.setOnTouchListener(new ImageButtonTouchListener(
                    (TransitionDrawable) ib.getDrawable()));
            ib.setOnFocusChangeListener(new ImageButtonFocusListener());
            ib.setTag(buttonData[i]);
            ib.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    DashboardButtonData data = (DashboardButtonData) v.getTag();
                    // null cookie means the service doesn't need an EID
                    if (data.authCookie == null) {
                        startActivity(data.intent);
                    } else {
                        if (settings.getBoolean("loginpref", false)) {
                            // persistent login
                            if (!data.authCookie.hasCookieBeenSet() || isLoggingIn()) {
                                showLoginFirstToast();
                            } else {
                                startActivity(data.intent);
                            }
                        } else {
                            // temp login
                            if (!data.authCookie.hasCookieBeenSet()) {
                                Intent login = new Intent(UTilitiesActivity.this,
                                        LoginActivity.class);
                                login.putExtra("activity", data.intent.getComponent()
                                        .getClassName());
                                login.putExtra("service", data.serviceChar);
                                startActivity(login);
                            } else {
                                startActivity(data.intent);
                            }
                        }
                    }
                }
            });
        }
    }

    /**
     * FocusListener that triggers a TransitionDrawable when the item goes
     * in and out of focus.
     */
    private class ImageButtonFocusListener implements OnFocusChangeListener {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus) {
                ((TransitionDrawable) ((ImageButton) v).getDrawable())
                        .startTransition(BUTTON_ANIMATION_DURATION);
            } else {
                ((TransitionDrawable) ((ImageButton) v).getDrawable())
                        .reverseTransition(BUTTON_ANIMATION_DURATION);
            }

        }
    }

    /**
     * TouchListener that triggers a TransitionDrawable on touch and release.
     */
    private class ImageButtonTouchListener implements OnTouchListener {
        private boolean buttonPressed = false;
        private TransitionDrawable crossfade;

        public ImageButtonTouchListener(TransitionDrawable transDrawable) {
            crossfade = transDrawable;
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    if (!buttonPressed) {
                        buttonPressed = true;
                        crossfade.startTransition(BUTTON_ANIMATION_DURATION);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (buttonPressed) {
                        buttonPressed = false;
                        crossfade.reverseTransition(BUTTON_ANIMATION_DURATION);
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    final int[] states = v.getDrawableState();
                    boolean pressedStateFound = false;
                    for (int state : states) {
                        if (state == android.R.attr.state_pressed) {
                            pressedStateFound = true;
                            if (!buttonPressed) {
                                buttonPressed = true;
                                crossfade.startTransition(BUTTON_ANIMATION_DURATION);
                            }
                            break;
                        }
                    }
                    if (!pressedStateFound && buttonPressed) {
                        buttonPressed = false;
                        crossfade.reverseTransition(BUTTON_ANIMATION_DURATION);
                    }
                    break;
            }
            return false;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = this.getSupportMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        boolean allLoggedIn = true;
        boolean anyLoggedIn = false;
        for (AuthCookie cookie : authCookies) {
            if (!cookie.hasCookieBeenSet()) {
                allLoggedIn = false;
            } else {
                anyLoggedIn = true;
            }
        }

        // update the displayed login state
        if (settings.getBoolean("loginpref", false)) {
            if (!isLoggingIn()) {
                if (allLoggedIn) {
                    replaceLoginButton(menu, R.id.logout_button, "Log out");
                } else {
                    replaceLoginButton(menu, R.id.login_button, "Log in");
                }
            } else {
                replaceLoginButton(menu, R.id.cancel_button, "Cancel");
            }
        } else {
            if (anyLoggedIn) {
                replaceLoginButton(menu, R.id.logout_button, "Log out");
            } else {
                menu.removeGroup(R.id.login_menu_group);
            }
        }
        return true;
    }

    /**
     * Replace current login button with another (typically "Log-in", "Cancel", or "Log out")
     * @param menu menu to add to
     * @param id id of button being added
     * @param text text of the button being added
     */
    private void replaceLoginButton(Menu menu, int id, String text) {
        menu.removeGroup(R.id.login_menu_group);
        menu.add(R.id.login_menu_group, id, Menu.NONE, text);
        MenuItem item = menu.findItem(id);
        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id) {
            case R.id.settings:
                loadSettings();
                break;
            case R.id.login_button:
                login();
                invalidateOptionsMenu();
                break;
            case R.id.logout_button:
                logout();
                invalidateOptionsMenu();
                break;
            case R.id.cancel_button:
                cancelLogin();
                invalidateOptionsMenu();
                break;
        }
        return true;
    }

    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        return loginTasks;
    }

    /**
     * Show "first launch" dialogs. Either the changelog for the first launch after an update
     * or the actual first-launch dialog letting the user known they should save their
     * credentials to get full use of the app.
     */
    private void handleFirstLaunch() {
        if (!settings.contains("firstRun")) {
            // first launch ever
            AlertDialog.Builder nologin_builder = new AlertDialog.Builder(this);
            nologin_builder<commit_msg>Send analytics event when user logs out<commit_after>
package com.nasageek.utexasutilities.activities;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.drawable.TransitionDrawable;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnTouchListener;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.Toast;

import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.Window;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.HitBuilders;
import com.nasageek.utexasutilities.AsyncTask;
import com.nasageek.utexasutilities.AuthCookie;
import com.nasageek.utexasutilities.ChangeLog;
import com.nasageek.utexasutilities.ChangeableContextTask;
import com.nasageek.utexasutilities.R;
import com.nasageek.utexasutilities.SecurePreferences;
import com.nasageek.utexasutilities.UTilitiesApplication;
import com.nasageek.utexasutilities.Utility;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import static com.nasageek.utexasutilities.UTilitiesApplication.BB_AUTH_COOKIE_KEY;
import static com.nasageek.utexasutilities.UTilitiesApplication.PNA_AUTH_COOKIE_KEY;
import static com.nasageek.utexasutilities.UTilitiesApplication.UTD_AUTH_COOKIE_KEY;

/**
 * Main entry point for UTilities. Allows the user to log in and contains a dashboard
 * of buttons to launch UT web services.
 */
public class UTilitiesActivity extends BaseActivity {

    private final static int BUTTON_ANIMATION_DURATION = 90;

    private final static String SECURE_PREF_PW_KEY = "com.nasageek.utexasutilities.password";

    private SharedPreferences settings;
    private Toast message;
    private ImageView scheduleCheck, balanceCheck, dataCheck, blackboardCheck;
    private AlertDialog nologin;

    private AuthCookie authCookies[];
    private List<AsyncTask> loginTasks;
    private UpdateUiTask updateUiTask;
    private AuthCookie utdAuthCookie;
    private AuthCookie pnaAuthCookie;
    private AuthCookie bbAuthCookie;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        UTilitiesApplication mApp = (UTilitiesApplication) getApplication();
        utdAuthCookie = mApp.getAuthCookie(UTD_AUTH_COOKIE_KEY);
        pnaAuthCookie = mApp.getAuthCookie(PNA_AUTH_COOKIE_KEY);
        bbAuthCookie = mApp.getAuthCookie(BB_AUTH_COOKIE_KEY);
        authCookies = new AuthCookie[]{utdAuthCookie, pnaAuthCookie, bbAuthCookie};

        loginTasks = (List<AsyncTask>) getLastCustomNonConfigurationInstance();
        if (loginTasks != null) {
            updateUiTask = (UpdateUiTask) loginTasks.get(0);
            if (updateUiTask != null) {
                updateUiTask.setContext(this);
            }
        }

        settings = PreferenceManager.getDefaultSharedPreferences(this.getBaseContext());
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setContentView(R.layout.main);
        setSupportProgressBarIndeterminateVisibility(isLoggingIn());

        // use one Activity-wide Toast so they don't stack up
        message = Toast.makeText(this, R.string.login_first, Toast.LENGTH_SHORT);

        handleUnencryptedPassword();
        handleFirstLaunch();
        if (settings.getBoolean("autologin", false) && !isLoggingIn() && !mApp.allCookiesSet()) {
            login();
        }
        setupDashBoardButtons();
    }

    /**
     * Set up the dashboard of buttons to launch the various services of the
     * app. This covers things like touch/focus listeners, authentication,
     * intent to launch, etc.
     */
    private void setupDashBoardButtons() {
        scheduleCheck = (ImageView) findViewById(R.id.scheduleCheck);
        balanceCheck = (ImageView) findViewById(R.id.balanceCheck);
        dataCheck = (ImageView) findViewById(R.id.dataCheck);
        blackboardCheck = (ImageView) findViewById(R.id.blackboardCheck);

        final Intent schedule = new Intent(this, ScheduleActivity.class);
        final Intent balance = new Intent(this, BalanceActivity.class);
        final Intent map = new Intent(this, CampusMapActivity.class);
        final Intent data = new Intent(this, DataUsageActivity.class);
        final Intent menu = new Intent(this, MenuActivity.class);
        final Intent blackboard = new Intent(this, BlackboardPanesActivity.class);

        // simple struct-like class to help handle related data
        class DashboardButtonData {
            public Intent intent;
            public int imageButtonId;
            public AuthCookie authCookie;
            public Character serviceChar;
            public ImageView checkOverlay;

            // for authenticated services
            public DashboardButtonData(Intent intent, int id, AuthCookie authCookie,
                                       Character service, ImageView check) {
                this.intent = intent;
                this.imageButtonId = id;
                this.authCookie = authCookie;
                this.serviceChar = service;
                this.checkOverlay = check;
            }

            // for unauthenticated services
            public DashboardButtonData(Intent intent, int id) {
                this(intent, id, null, null, null);
            }
        }

        DashboardButtonData buttonData[] = new DashboardButtonData[6];
        buttonData[0] = new DashboardButtonData(schedule, R.id.schedule_button, utdAuthCookie, 'u',
                scheduleCheck);
        buttonData[1] = new DashboardButtonData(balance, R.id.balance_button, utdAuthCookie, 'u',
                balanceCheck);
        buttonData[2] = new DashboardButtonData(blackboard, R.id.blackboard_button, bbAuthCookie,
                'b', blackboardCheck);
        buttonData[3] = new DashboardButtonData(data, R.id.data_button, pnaAuthCookie, 'p',
                dataCheck);
        buttonData[4] = new DashboardButtonData(map, R.id.map_button);
        buttonData[5] = new DashboardButtonData(menu, R.id.menu_button);

        for (int i = 0; i < 6; i++) {
            ImageButton ib = (ImageButton) findViewById(buttonData[i].imageButtonId);
            ib.setOnTouchListener(new ImageButtonTouchListener(
                    (TransitionDrawable) ib.getDrawable()));
            ib.setOnFocusChangeListener(new ImageButtonFocusListener());
            ib.setTag(buttonData[i]);
            ib.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    DashboardButtonData data = (DashboardButtonData) v.getTag();
                    // null cookie means the service doesn't need an EID
                    if (data.authCookie == null) {
                        startActivity(data.intent);
                    } else {
                        if (settings.getBoolean("loginpref", false)) {
                            // persistent login
                            if (!data.authCookie.hasCookieBeenSet() || isLoggingIn()) {
                                showLoginFirstToast();
                            } else {
                                startActivity(data.intent);
                            }
                        } else {
                            // temp login
                            if (!data.authCookie.hasCookieBeenSet()) {
                                Intent login = new Intent(UTilitiesActivity.this,
                                        LoginActivity.class);
                                login.putExtra("activity", data.intent.getComponent()
                                        .getClassName());
                                login.putExtra("service", data.serviceChar);
                                startActivity(login);
                            } else {
                                startActivity(data.intent);
                            }
                        }
                    }
                }
            });
        }
    }

    /**
     * FocusListener that triggers a TransitionDrawable when the item goes
     * in and out of focus.
     */
    private class ImageButtonFocusListener implements OnFocusChangeListener {
        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (hasFocus) {
                ((TransitionDrawable) ((ImageButton) v).getDrawable())
                        .startTransition(BUTTON_ANIMATION_DURATION);
            } else {
                ((TransitionDrawable) ((ImageButton) v).getDrawable())
                        .reverseTransition(BUTTON_ANIMATION_DURATION);
            }

        }
    }

    /**
     * TouchListener that triggers a TransitionDrawable on touch and release.
     */
    private class ImageButtonTouchListener implements OnTouchListener {
        private boolean buttonPressed = false;
        private TransitionDrawable crossfade;

        public ImageButtonTouchListener(TransitionDrawable transDrawable) {
            crossfade = transDrawable;
        }

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            switch (event.getActionMasked()) {
                case MotionEvent.ACTION_DOWN:
                    if (!buttonPressed) {
                        buttonPressed = true;
                        crossfade.startTransition(BUTTON_ANIMATION_DURATION);
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    if (buttonPressed) {
                        buttonPressed = false;
                        crossfade.reverseTransition(BUTTON_ANIMATION_DURATION);
                    }
                    break;
                case MotionEvent.ACTION_MOVE:
                    final int[] states = v.getDrawableState();
                    boolean pressedStateFound = false;
                    for (int state : states) {
                        if (state == android.R.attr.state_pressed) {
                            pressedStateFound = true;
                            if (!buttonPressed) {
                                buttonPressed = true;
                                crossfade.startTransition(BUTTON_ANIMATION_DURATION);
                            }
                            break;
                        }
                    }
                    if (!pressedStateFound && buttonPressed) {
                        buttonPressed = false;
                        crossfade.reverseTransition(BUTTON_ANIMATION_DURATION);
                    }
                    break;
            }
            return false;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = this.getSupportMenuInflater();
        inflater.inflate(R.menu.main_menu, menu);
        boolean allLoggedIn = true;
        boolean anyLoggedIn = false;
        for (AuthCookie cookie : authCookies) {
            if (!cookie.hasCookieBeenSet()) {
                allLoggedIn = false;
            } else {
                anyLoggedIn = true;
            }
        }

        // update the displayed login state
        if (settings.getBoolean("loginpref", false)) {
            if (!isLoggingIn()) {
                if (allLoggedIn) {
                    replaceLoginButton(menu, R.id.logout_button, "Log out");
                } else {
                    replaceLoginButton(menu, R.id.login_button, "Log in");
                }
            } else {
                replaceLoginButton(menu, R.id.cancel_button, "Cancel");
            }
        } else {
            if (anyLoggedIn) {
                replaceLoginButton(menu, R.id.logout_button, "Log out");
            } else {
                menu.removeGroup(R.id.login_menu_group);
            }
        }
        return true;
    }

    /**
     * Replace current login button with another (typically "Log-in", "Cancel", or "Log out")
     * @param menu menu to add to
     * @param id id of button being added
     * @param text text of the button being added
     */
    private void replaceLoginButton(Menu menu, int id, String text) {
        menu.removeGroup(R.id.login_menu_group);
        menu.add(R.id.login_menu_group, id, Menu.NONE, text);
        MenuItem item = menu.findItem(id);
        item.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id) {
            case R.id.settings:
                loadSettings();
                break;
            case R.id.login_button:
                login();
                invalidateOptionsMenu();
                break;
            case R.id.logout_button:
                analyticsTracker.send(new HitBuilders.EventBuilder()
                        .setCategory(this.getLocalClassName())
                        .setAction("Logout")
                        .build());
                GoogleAnalytics.getInstance(this).dispatchLocalHits();
                logout();
                invalidateOptionsMenu();
                break;
            case R.id.cancel_button:
                cancelLogin();
                invalidateOptionsMenu();
                break;
        }
        return true;
    }

    @Override
    public Object onRetainCustomNonConfigurationInstance() {
        return loginTasks;
    }

    /**
     * Show "first launch" dialogs. Either the changelog for the first launch after an update
     * or the actual first-launch dialog letting the user known they should save their
     * credentials to get full use of the app.
     */
    private void handleFirstLaunch() {
        if (!settings.contains("firstRun")) {
            // first launch ever
            AlertDialog.Builder nologin_builder = new AlertDialog.Builder(this);
            nologin_builder
{'fraction_non_alphanumeric': 0.08060839210657549, 'fraction_numerical': 0.0008548835221201111, 'mean_word_length': 1.9183991683991684, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2497064', 'n_tokens_mistral': 7217, 'n_tokens_neox': 6896, 'n_words': 1567}