<commit_before>package org.commcare.activities;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Toast;

import org.commcare.CommCareApplication;
import org.commcare.activities.components.FormEntryConstants;
import org.commcare.activities.components.FormEntryInstanceState;
import org.commcare.activities.components.FormEntrySessionWrapper;
import org.commcare.android.database.app.models.UserKeyRecord;
import org.commcare.android.database.user.models.FormRecord;
import org.commcare.android.database.user.models.SessionStateDescriptor;
import org.commcare.core.process.CommCareInstanceInitializer;
import org.commcare.dalvik.BuildConfig;
import org.commcare.dalvik.R;
import org.commcare.google.services.ads.AdMobManager;
import org.commcare.google.services.analytics.AnalyticsParamValue;
import org.commcare.google.services.analytics.FirebaseAnalyticsUtil;
import org.commcare.heartbeat.UpdatePromptHelper;
import org.commcare.interfaces.CommCareActivityUIController;
import org.commcare.models.AndroidSessionWrapper;
import org.commcare.models.database.SqlStorage;
import org.commcare.preferences.AdvancedActionsPreferences;
import org.commcare.preferences.CommCarePreferences;
import org.commcare.preferences.DeveloperPreferences;
import org.commcare.provider.FormsProviderAPI;
import org.commcare.provider.InstanceProviderAPI;
import org.commcare.session.CommCareSession;
import org.commcare.session.SessionFrame;
import org.commcare.session.SessionNavigationResponder;
import org.commcare.session.SessionNavigator;
import org.commcare.suite.model.EntityDatum;
import org.commcare.suite.model.Entry;
import org.commcare.suite.model.PostRequest;
import org.commcare.suite.model.RemoteRequestEntry;
import org.commcare.suite.model.SessionDatum;
import org.commcare.suite.model.StackFrameStep;
import org.commcare.suite.model.Text;
import org.commcare.tasks.FormLoaderTask;
import org.commcare.tasks.FormRecordCleanupTask;
import org.commcare.util.LogTypes;
import org.commcare.utils.AndroidCommCarePlatform;
import org.commcare.utils.AndroidInstanceInitializer;
import org.commcare.utils.ChangeLocaleUtil;
import org.commcare.utils.CrashUtil;
import org.commcare.utils.EntityDetailUtils;
import org.commcare.utils.GlobalConstants;
import org.commcare.utils.SessionUnavailableException;
import org.commcare.utils.StorageUtils;
import org.commcare.views.UserfacingErrorHandling;
import org.commcare.views.dialogs.CommCareAlertDialog;
import org.commcare.views.dialogs.DialogChoiceItem;
import org.commcare.views.dialogs.DialogCreationHelpers;
import org.commcare.views.dialogs.PaneledChoiceDialog;
import org.commcare.views.dialogs.StandardAlertDialog;
import org.commcare.views.notifications.NotificationMessageFactory;
import org.javarosa.core.model.User;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.core.services.Logger;
import org.javarosa.core.services.locale.Localization;
import org.javarosa.xpath.XPathTypeMismatchException;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Vector;

/**
 * Manages all of the shared (mostly non-UI) components of a CommCare home screen:
 * activity lifecycle, implementation of available actions, session navigation, etc.
 */
public abstract class HomeScreenBaseActivity<T> extends SyncCapableCommCareActivity<T>
        implements SessionNavigationResponder {

    /**
     * Request code for launching a menu list or menu grid
     */
    public static final int GET_COMMAND = 1;
    /**
     * Request code for launching EntitySelectActivity (to allow user to select a case),
     * or EntityDetailActivity (to allow user to confirm an auto-selected case)
     */
    protected static final int GET_CASE = 2;
    protected static final int GET_REMOTE_DATA = 3;
    /**
     * Request code for launching FormEntryActivity
     */
    protected static final int MODEL_RESULT = 4;
    protected static final int MAKE_REMOTE_POST = 5;
    public static final int GET_INCOMPLETE_FORM = 6;
    protected static final int PREFERENCES_ACTIVITY = 7;
    protected static final int ADVANCED_ACTIONS_ACTIVITY = 8;
    protected static final int CREATE_PIN = 9;
    protected static final int AUTHENTICATION_FOR_PIN = 10;

    private static final String KEY_PENDING_SESSION_DATA = "pending-session-data-id";
    private static final String KEY_PENDING_SESSION_DATUM_ID = "pending-session-datum-id";

    /**
     * Restart is a special CommCare activity result code which means that the session was
     * invalidated in the calling activity and that the current session should be resynced
     */
    public static final int RESULT_RESTART = 3;

    private int mDeveloperModeClicks = 0;

    private SessionNavigator sessionNavigator;
    private boolean sessionNavigationProceedingAfterOnResume;

    private boolean loginExtraWasConsumed;
    private static final String EXTRA_CONSUMED_KEY = "login_extra_was_consumed";
    private boolean isRestoringSession = false;

    // The API allows for external calls. When this occurs, redispatch to their
    // activity instead of commcare.
    private boolean wasExternal = false;
    private static final String WAS_EXTERNAL_KEY = "was_external";

    @Override
    protected void onCreateSessionSafe(Bundle savedInstanceState) {
        super.onCreateSessionSafe(savedInstanceState);
        loadInstanceState(savedInstanceState);
        CrashUtil.registerAppData();
        AdMobManager.initAdsForCurrentConsumerApp(getApplicationContext());
        sessionNavigator = new SessionNavigator(this);

        processFromExternalLaunch(savedInstanceState);
        processFromShortcutLaunch();
        processFromLoginLaunch();
    }

    private void loadInstanceState(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            loginExtraWasConsumed = savedInstanceState.getBoolean(EXTRA_CONSUMED_KEY);
            wasExternal = savedInstanceState.getBoolean(WAS_EXTERNAL_KEY);
        }
    }

    /**
     * Set state that signifies activity was launch from external app.
     */
    private void processFromExternalLaunch(Bundle savedInstanceState) {
        if (savedInstanceState == null && getIntent().hasExtra(DispatchActivity.WAS_EXTERNAL)) {
            wasExternal = true;
            sessionNavigator.startNextSessionStep();
        }
    }

    private void processFromShortcutLaunch() {
        if (getIntent().getBooleanExtra(DispatchActivity.WAS_SHORTCUT_LAUNCH, false)) {
            sessionNavigator.startNextSessionStep();
        }
    }

    private void processFromLoginLaunch() {
        if (getIntent().getBooleanExtra(DispatchActivity.START_FROM_LOGIN, false) &&
                !loginExtraWasConsumed) {

            getIntent().removeExtra(DispatchActivity.START_FROM_LOGIN);
            loginExtraWasConsumed = true;

            if (tryRestoringFormFromSessionExpiration()) {
                return;
            }

            if (tryRestoringSession()) {
                return;
            }

            // Trigger off a regular unsent task processor, unless we're about to sync (which will
            // then handle this in a blocking fashion)
            if (!CommCareApplication.instance().isSyncPending(false)) {
                checkAndStartUnsentFormsTask(false, false);
            }

            if (isDemoUser()) {
                showDemoModeWarning();
                return;
            }
            if (checkForPinLaunchConditions()) {
                return;
            }
        }
    }

    private boolean tryRestoringSession() {
        CommCareSession session = CommCareApplication.instance().getCurrentSession();
        if (session.getCommand() != null) {
            // Restore the session state if there is a command. This is for debugging and
            // occurs when a serialized session was stored by a previous user session
            isRestoringSession = true;
            sessionNavigator.startNextSessionStep();
            return true;
        }
        return false;
    }
<commit_msg>be clearer about the ordering of login launch actions<commit_after>package org.commcare.activities;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Toast;

import org.commcare.CommCareApplication;
import org.commcare.activities.components.FormEntryConstants;
import org.commcare.activities.components.FormEntryInstanceState;
import org.commcare.activities.components.FormEntrySessionWrapper;
import org.commcare.android.database.app.models.UserKeyRecord;
import org.commcare.android.database.user.models.FormRecord;
import org.commcare.android.database.user.models.SessionStateDescriptor;
import org.commcare.core.process.CommCareInstanceInitializer;
import org.commcare.dalvik.BuildConfig;
import org.commcare.dalvik.R;
import org.commcare.google.services.ads.AdMobManager;
import org.commcare.google.services.analytics.AnalyticsParamValue;
import org.commcare.google.services.analytics.FirebaseAnalyticsUtil;
import org.commcare.heartbeat.UpdatePromptHelper;
import org.commcare.interfaces.CommCareActivityUIController;
import org.commcare.models.AndroidSessionWrapper;
import org.commcare.models.database.SqlStorage;
import org.commcare.preferences.AdvancedActionsPreferences;
import org.commcare.preferences.CommCarePreferences;
import org.commcare.preferences.DeveloperPreferences;
import org.commcare.provider.FormsProviderAPI;
import org.commcare.provider.InstanceProviderAPI;
import org.commcare.session.CommCareSession;
import org.commcare.session.SessionFrame;
import org.commcare.session.SessionNavigationResponder;
import org.commcare.session.SessionNavigator;
import org.commcare.suite.model.EntityDatum;
import org.commcare.suite.model.Entry;
import org.commcare.suite.model.PostRequest;
import org.commcare.suite.model.RemoteRequestEntry;
import org.commcare.suite.model.SessionDatum;
import org.commcare.suite.model.StackFrameStep;
import org.commcare.suite.model.Text;
import org.commcare.tasks.FormLoaderTask;
import org.commcare.tasks.FormRecordCleanupTask;
import org.commcare.util.LogTypes;
import org.commcare.utils.AndroidCommCarePlatform;
import org.commcare.utils.AndroidInstanceInitializer;
import org.commcare.utils.ChangeLocaleUtil;
import org.commcare.utils.CrashUtil;
import org.commcare.utils.EntityDetailUtils;
import org.commcare.utils.GlobalConstants;
import org.commcare.utils.SessionUnavailableException;
import org.commcare.utils.StorageUtils;
import org.commcare.views.UserfacingErrorHandling;
import org.commcare.views.dialogs.CommCareAlertDialog;
import org.commcare.views.dialogs.DialogChoiceItem;
import org.commcare.views.dialogs.DialogCreationHelpers;
import org.commcare.views.dialogs.PaneledChoiceDialog;
import org.commcare.views.dialogs.StandardAlertDialog;
import org.commcare.views.notifications.NotificationMessageFactory;
import org.javarosa.core.model.User;
import org.javarosa.core.model.condition.EvaluationContext;
import org.javarosa.core.model.instance.TreeReference;
import org.javarosa.core.services.Logger;
import org.javarosa.core.services.locale.Localization;
import org.javarosa.xpath.XPathTypeMismatchException;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Vector;

/**
 * Manages all of the shared (mostly non-UI) components of a CommCare home screen:
 * activity lifecycle, implementation of available actions, session navigation, etc.
 */
public abstract class HomeScreenBaseActivity<T> extends SyncCapableCommCareActivity<T>
        implements SessionNavigationResponder {

    /**
     * Request code for launching a menu list or menu grid
     */
    public static final int GET_COMMAND = 1;
    /**
     * Request code for launching EntitySelectActivity (to allow user to select a case),
     * or EntityDetailActivity (to allow user to confirm an auto-selected case)
     */
    protected static final int GET_CASE = 2;
    protected static final int GET_REMOTE_DATA = 3;
    /**
     * Request code for launching FormEntryActivity
     */
    protected static final int MODEL_RESULT = 4;
    protected static final int MAKE_REMOTE_POST = 5;
    public static final int GET_INCOMPLETE_FORM = 6;
    protected static final int PREFERENCES_ACTIVITY = 7;
    protected static final int ADVANCED_ACTIONS_ACTIVITY = 8;
    protected static final int CREATE_PIN = 9;
    protected static final int AUTHENTICATION_FOR_PIN = 10;

    private static final String KEY_PENDING_SESSION_DATA = "pending-session-data-id";
    private static final String KEY_PENDING_SESSION_DATUM_ID = "pending-session-datum-id";

    /**
     * Restart is a special CommCare activity result code which means that the session was
     * invalidated in the calling activity and that the current session should be resynced
     */
    public static final int RESULT_RESTART = 3;

    private int mDeveloperModeClicks = 0;

    private SessionNavigator sessionNavigator;
    private boolean sessionNavigationProceedingAfterOnResume;

    private boolean loginExtraWasConsumed;
    private static final String EXTRA_CONSUMED_KEY = "login_extra_was_consumed";
    private boolean isRestoringSession = false;

    // The API allows for external calls. When this occurs, redispatch to their
    // activity instead of commcare.
    private boolean wasExternal = false;
    private static final String WAS_EXTERNAL_KEY = "was_external";

    @Override
    protected void onCreateSessionSafe(Bundle savedInstanceState) {
        super.onCreateSessionSafe(savedInstanceState);
        loadInstanceState(savedInstanceState);
        CrashUtil.registerAppData();
        AdMobManager.initAdsForCurrentConsumerApp(getApplicationContext());
        sessionNavigator = new SessionNavigator(this);

        processFromExternalLaunch(savedInstanceState);
        processFromShortcutLaunch();
        processFromLoginLaunch();
    }

    private void loadInstanceState(Bundle savedInstanceState) {
        if (savedInstanceState != null) {
            loginExtraWasConsumed = savedInstanceState.getBoolean(EXTRA_CONSUMED_KEY);
            wasExternal = savedInstanceState.getBoolean(WAS_EXTERNAL_KEY);
        }
    }

    /**
     * Set state that signifies activity was launch from external app.
     */
    private void processFromExternalLaunch(Bundle savedInstanceState) {
        if (savedInstanceState == null && getIntent().hasExtra(DispatchActivity.WAS_EXTERNAL)) {
            wasExternal = true;
            sessionNavigator.startNextSessionStep();
        }
    }

    private void processFromShortcutLaunch() {
        if (getIntent().getBooleanExtra(DispatchActivity.WAS_SHORTCUT_LAUNCH, false)) {
            sessionNavigator.startNextSessionStep();
        }
    }

    private void processFromLoginLaunch() {
        if (getIntent().getBooleanExtra(DispatchActivity.START_FROM_LOGIN, false) &&
                !loginExtraWasConsumed) {
            getIntent().removeExtra(DispatchActivity.START_FROM_LOGIN);
            loginExtraWasConsumed = true;
            doLoginLaunchChecksInOrder();
        }
    }

    /**
     * The order of operations in this method is very deliberate, and the logic for it is as
     * follows:
     * - If we're in demo mode, then we don't want to do any of the other checks because they're
     * not relevant
     * - Form and session restorations need to happen before we try to sync, because once we sync
     * it could invalidate those states.
     * - tryRestoringFormFromSessionExpiration() comes before tryRestoringSession() because it is
     * of higher importance
     * - Once we're past the first 3, starting a background form-send process is safe
     */
    private void doLoginLaunchChecksInOrder() {
        if (isDemoUser()) {
            showDemoModeWarning();
            return;
        }

        if (tryRestoringFormFromSessionExpiration()) {
            return;
        }

        if (tryRestoringSession()) {
            return;
        }

        if (!CommCareApplication.instance().isSyncPending(false)) {
            // Trigger off a regular unsent task processor, unless we're about to sync (which will
            // then handle this in a blocking fashion)
            checkAndStartUnsentFormsTask(false, false);
        }

        if (checkForPinLaunchConditions()) {
            return;
        }
    }

    private boolean tryRestoringSession() {
        CommCareSession session = CommCareApplication.instance().getCurrentSession();
        if (session.getCommand() != null) {
            // Restore the session state if there is a command. This is for debugging and
            // occurs when a serialized session was stored by a previous user session
            isRestoringSession = true;
            sessionNavigator.startNextSessionStep();
            return true;
        }
        return false;
    }

{'fraction_non_alphanumeric': 0.07717151113377528, 'fraction_numerical': 0.001756473454586662, 'mean_word_length': 3.908231368186874, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1561876', 'n_tokens_mistral': 4816, 'n_tokens_neox': 4654, 'n_words': 1128}