package org.commcare.activities;
import android.Manifest;
import android.app.ActionBar;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import org.commcare.AppUtils;
import org.commcare.CommCareApp;
import org.commcare.CommCareApplication;
import org.commcare.dalvik.BuildConfig;
import org.commcare.dalvik.R;
import org.commcare.engine.resource.AppInstallStatus;
import org.commcare.engine.resource.installers.SingleAppInstallation;
import org.commcare.fragments.ContainerFragment;
import org.commcare.fragments.InstallConfirmFragment;
import org.commcare.fragments.InstallPermissionsFragment;
import org.commcare.fragments.SelectInstallModeFragment;
import org.commcare.fragments.SetupEnterURLFragment;
import org.commcare.interfaces.RuntimePermissionRequester;
import org.commcare.google.services.analytics.GoogleAnalyticsFields;
import org.commcare.google.services.analytics.GoogleAnalyticsUtils;
import org.commcare.android.database.global.models.ApplicationRecord;
import org.commcare.preferences.GlobalPrivilegesManager;
import org.commcare.resources.model.InvalidResourceException;
import org.commcare.resources.model.UnresolvedResourceException;
import org.commcare.tasks.ResourceEngineListener;
import org.commcare.tasks.ResourceEngineTask;
import org.commcare.tasks.RetrieveParseVerifyMessageListener;
import org.commcare.tasks.RetrieveParseVerifyMessageTask;
import org.commcare.utils.ConsumerAppsUtil;
import org.commcare.utils.MultipleAppsUtil;
import org.commcare.utils.Permissions;
import org.commcare.views.ManagedUi;
import org.commcare.views.dialogs.CustomProgressDialog;
import org.commcare.views.dialogs.DialogCreationHelpers;
import org.commcare.views.notifications.NotificationMessage;
import org.commcare.views.notifications.NotificationMessageFactory;
import org.javarosa.core.reference.InvalidReferenceException;
import org.javarosa.core.reference.ReferenceManager;
import org.javarosa.core.services.locale.Localization;
import org.javarosa.core.util.PropertyUtils;
import java.io.IOException;
import java.security.SignatureException;
import java.util.List;
/**
* Responsible for identifying the state of the application (uninstalled,
* installed) and performing any necessary setup to get to a place where
* CommCare can load normally.
*
* If the startup activity identifies that the app is installed properly it
* should not ever require interaction or be visible to the user.
*
* @author ctsims
*/
@ManagedUi(R.layout.first_start_screen_modern)
public class CommCareSetupActivity extends CommCareActivity<CommCareSetupActivity>
implements ResourceEngineListener, SetupEnterURLFragment.URLInstaller,
InstallConfirmFragment.StartStopInstallCommands, RetrieveParseVerifyMessageListener,
RuntimePermissionRequester {
private static final String TAG = CommCareSetupActivity.class.getSimpleName();
private static final String KEY_UI_STATE = "current_install_ui_state";
private static final String KEY_LAST_INSTALL_MODE = "offline_install";
private static final String KEY_FROM_EXTERNAL = "from_external";
private static final String KEY_FROM_MANAGER = "from_manager";
private static final String KEY_MANUAL_SMS_INSTALL = "sms-install-triggered-manually";
private static final String KEY_ERROR_MESSAGE = "error-message";
private static final int SMS_PERMISSIONS_REQUEST = 2;
private static final String FORCE_VALIDATE_KEY = "validate";
/**
* UI configuration states.
*/
public enum UiState {
IN_URL_ENTRY,
CHOOSE_INSTALL_ENTRY_METHOD,
READY_TO_INSTALL,
NEEDS_PERMS,
BLANK
}
private UiState uiState = UiState.CHOOSE_INSTALL_ENTRY_METHOD;
private String errorMessageToDisplay;
public static final int MENU_ARCHIVE = Menu.FIRST;
private static final int MENU_SMS = Menu.FIRST + 2;
private static final int MENU_FROM_LIST = Menu.FIRST + 3;
// Activity request codes
public static final int BARCODE_CAPTURE = 1;
public static final int OFFLINE_INSTALL = 3;
private static final int MULTIPLE_APPS_LIMIT = 4;
public static final int GET_APPS_FROM_HQ = 5;
// dialog ID
private static final int DIALOG_INSTALL_PROGRESS = 4;
private boolean startAllowed = true;
private String incomingRef;
private CommCareApp ccApp;
/**
* Indicates that this activity was launched from the AppManagerActivity
*/
private boolean fromManager;
/**
* Indicates that this activity was launched from an outside application (such as a bit.ly
* url entered in a browser)
*/
private boolean fromExternal;
private static final int INSTALL_MODE_BARCODE = 0;
private static final int INSTALL_MODE_URL = 1;
private static final int INSTALL_MODE_OFFLINE = 2;
private static final int INSTALL_MODE_SMS = 3;
private static final int INSTALL_MODE_FROM_LIST = 4;
private int lastInstallMode;
/**
* Remember how the sms install was triggered in case orientation changes while asking for permissions
*/
private boolean manualSMSInstall;
private final FragmentManager fm = getSupportFragmentManager();
private final InstallConfirmFragment startInstall = new InstallConfirmFragment();
private final SelectInstallModeFragment installFragment = new SelectInstallModeFragment();
private final InstallPermissionsFragment permFragment = new InstallPermissionsFragment();
private ContainerFragment<CommCareApp> containerFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fromManager = getIntent().getBooleanExtra(AppManagerActivity.KEY_LAUNCH_FROM_MANAGER, false);
if (checkForMultipleAppsViolation()) {
return;
}
loadIntentAndInstanceState(savedInstanceState);
persistCommCareAppState();
if (isSingleAppBuild()) {
uiState = UiState.BLANK;
}
boolean askingForPerms =
Permissions.acquireAllAppPermissions(this, this,
Permissions.ALL_PERMISSIONS_REQUEST);
if (!askingForPerms) {
if (isSingleAppBuild()) {
SingleAppInstallation.installSingleApp(this, DIALOG_INSTALL_PROGRESS);
} else if (uiState == UiState.CHOOSE_INSTALL_ENTRY_METHOD) {
// Don't perform SMS install if we aren't on base setup state
// (i.e. in the middle of an install)
// With basic perms satisfied, ask user to allow SMS reading
// for sms app install code
performSMSInstall(false);
}
}
}
private void loadIntentAndInstanceState(Bundle savedInstanceState) {
if (savedInstanceState == null) {
if (Intent.ACTION_VIEW.equals(this.getIntent().getAction())) {
//We got called from an outside application, it's gonna be a wild ride!
fromExternal = true;
incomingRef = this.getIntent().getData().toString();
if (incomingRef.contains(".ccz")) {
// make sure this is in the file system
boolean isFile = incomingRef.contains("file://");
if (isFile) {
// remove file:// prepend
incomingRef = incomingRef.substring(incomingRef.indexOf("//") + 2);
Intent i = new Intent(this, InstallArchiveActivity.class);
i.putExtra(InstallArchiveActivity.ARCHIVE_FILEPATH, incomingRef);
startActivityForResult(i, OFFLINE_INSTALL);
} else {
// currently down allow other locations like http://
fail(NotificationMessageFactory.message(NotificationMessageFactory.StockMessages.Bad_Archive_File), true);
}
} else {
this.uiState = UiState.READY_TO_INSTALL;
}
}
} else {
loadStateFromInstance(savedInstanceState);
}
}
private void loadStateFromInstance(Bundle savedInstanceState) {
uiState = (UiState)savedInstanceState.getSerializable(KEY_UI_STATE);
incomingRef = savedInstanceState.getString("profileref");
fromExternal = savedInstanceState.getBoolean(KEY_FROM_EXTERNAL);
fromManager = savedInstanceState.getBoolean(KEY_FROM_MANAGER);
manualSMSInstall = savedInstanceState.getBoolean(KEY_MANUAL_SMS_INSTALL);
lastInstallMode = savedInstanceState.getInt(KEY_LAST_INSTALL_MODE);
errorMessageToDisplay = savedInstanceState.getString(KEY_ERROR_MESSAGE);
// Uggggh, this might not be 100% legit depending on timing, what
// if we've already reconnected and shut down the dialog?
startAllowed = savedInstanceState.getBoolean("startAllowed");
}
private void persistCommCareAppState() {
FragmentManager fm = this.getSupportFragmentManager();
containerFragment = (ContainerFragment<CommCareApp>)fm.findFragmentByTag("cc-app");
if (containerFragment == null) {
containerFragment = new ContainerFragment<>();
fm.beginTransaction().add(containerFragment, "cc-app").commit();
} else {
ccApp = containerFragment.getData();
}
}
/**
* @return if installation is not allowed due to multiple apps limitations
*/
private boolean checkForMultipleAppsViolation() {
if (AppUtils.getInstalledAppRecords().size() >= 2
&& !GlobalPrivilegesManager.isMultipleAppsPrivilegeEnabled()
&& !BuildConfig.DEBUG) {
Intent i = new Intent(this, MultipleAppsLimitWarningActivity.class);
i.putExtra(AppManagerActivity.KEY_LAUNCH_FROM_MANAGER, fromManager);
startActivityForResult(i, MULTIPLE_APPS_LIMIT);
return true;
}
return false;
}
@Override
public void onAttachFragment(Fragment fragment) {
super.onAttachFragment(fragment);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ActionBar actionBar = getActionBar();
if (actionBar != null) {
// removes the back button from the action bar
actionBar.setDisplayHomeAsUpEnabled(false);
}
}
}
@Override
protected void onResume() {
super.onResume();
if (!fromManager && !fromExternal && MultipleAppsUtil.usableAppsPresent()) {
// If clicking the regular app icon brought us to CommCareSetupActivity
// (because that's where we were last time the app was up), but there are now
// 1 or more available apps, we want to fall back to dispatch activity
setResult(RESULT_OK);
finish();
}
}
@Override
protected void onResumeFragments() {
super.onResumeFragments();
installFragment.showOrHideErrorMessage();
uiStateScreenTransition();
}
@Override
public void onURLChosen(String url) {
incomingRef = url;
this.uiState = UiState.READY_TO_INSTALL;
uiStateScreenTransition();
}
private void uiStateScreenTransition() {
if (areFragmentsPaused()) {
// Don't perform fragment transactions when the activity isn't visible
return;
}
Fragment fragment;
FragmentTransaction ft = fm.beginTransaction();
switch (uiState) {
case READY_TO_INSTALL:
if (incomingRef == null || incomingRef.length() == 0) {
Log.e(TAG, "During install: incomingRef is empty!");
displayError("Empty URL provided");
return;
}
// the buttonCommands were already set when the fragment was
// attached, no need to set them here
fragment = startInstall;
break;
case IN_URL_ENTRY:
fragment = restoreInstallSetupFragment();
break;
case CHOOSE_INSTALL_ENTRY_METHOD:
fragment = installFragment;
break;
case NEEDS_PERMS:
fragment = permFragment;
break;
case BLANK:
fragment = new Fragment();
break;
default:
return;
}
ft.replace(R.id.setup_fragment_container, fragment);
ft.commit();
}
private Fragment restoreInstallSetupFragment() {
Fragment fragment = null;
List<Fragment> fgmts = fm.getFragments();
int lastIndex = fgmts != null ? fgmts.size() - 1 : -1;
if (lastIndex > -1) {
fragment = fgmts.get(lastIndex);
}
if (!(fragment instanceof SetupEnterURLFragment)) {
// last fragment wasn't url entry, so default to the installation method chooser
fragment = installFragment;
}
return fragment;
}
@Override
public int getWakeLockLevel() {
return PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(KEY_UI_STATE, uiState);
outState.putString("profileref", incomingRef);
outState.putBoolean("startAllowed", startAllowed);
outState.putInt(KEY_LAST_INSTALL_MODE, lastInstallMode);
outState.putBoolean(KEY_FROM_EXTERNAL, fromExternal);
outState.putBoolean(KEY_FROM_MANAGER, fromManager);
outState.putBoolean(KEY_MANUAL_SMS_INSTALL, manualSMSInstall);
outState.putString(KEY_ERROR_MESSAGE, errorMessageToDisplay);
Log.v("UiState", "Saving instance state: " + outState);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
String result = null;
switch (requestCode) {
case BARCODE_CAPTURE:
if (resultCode == Activity.RESULT_OK) {
result = data.getStringExtra("SCAN_RESULT");
String dbg = "Got url from barcode scanner: " + result;
Log.i(TAG, dbg);
lastInstallMode = INSTALL_MODE_BARCODE;
}
break;
case OFFLINE_INSTALL:
if (resultCode == Activity.RESULT_OK) {
lastInstallMode = INSTALL_MODE_OFFLINE;
result = data.getStringExtra(InstallArchiveActivity.ARCHIVE_JR_REFERENCE);
}
break;
case GET_APPS_FROM_HQ:
if (resultCode == Activity.RESULT_OK) {
lastInstallMode = INSTALL_MODE_FROM_LIST;
result = data.getStringExtra(InstallFromListActivity.PROFILE_REF);
}
break;
case MULTIPLE_APPS_LIMIT:
setResult(RESULT_CANCELED);
finish();
return;
}
if (result == null) {
return;
}
if (lastInstallMode == INSTALL_MODE_FROM_LIST) {
GoogleAnalyticsUtils.reportFeatureUsage(GoogleAnalyticsFields.ACTION_INSTALL_FROM_LIST);
}
setReadyToInstall(result);
}
private void setReadyToInstall(String reference) {
incomingRef = reference;
this.uiState = UiState.READY_TO_INSTALL;
try {
ReferenceManager.instance().DeriveReference(incomingRef);
if (lastInstallMode == INSTALL_MODE_OFFLINE) {
onStartInstallClicked();
} else {
uiStateScreenTransition();
}
} catch (InvalidReferenceException ire) {
incomingRef = null;
fail(Localization.get("install.bad.ref"));
}
}
@Override
public void startBlockingForTask(int id) {
super.startBlockingForTask(id);
this.startAllowed = false;
}
@Override
public void stopBlockingForTask(int id) {
super.stopBlockingForTask(id);
this.startAllowed = true;
}
private void startResourceInstall() {
if (startAllowed) {
ccApp = getCommCareApp();
containerFragment.setData(ccApp);
CustomProgressDialog lastDialog = getCurrentProgressDialog();
// used to tell the ResourceEngineTask whether or not it should
// sleep before it starts, set based on whether we are currently
// in keep trying mode.
boolean shouldSleep = (lastDialog != null) && lastDialog.isChecked();
ResourceEngineTask<CommCareSetupActivity> task =
new ResourceEngineTask<CommCareSetupActivity>(ccApp,
DIALOG_INSTALL_PROGRESS, shouldSleep) {
@Override
protected void deliverResult(CommCareSetupActivity receiver,
AppInstallStatus result) {
switch (result) {
case Installed:
receiver.reportSuccess(true);
break;
case UpToDate:
receiver.reportSuccess(false);
break;
case MissingResourcesWithMessage:
// fall through to more general case:
case MissingResources:
receiver.failMissingResource(this.missingResourceException, result);
break;
case InvalidResource:
receiver.failInvalidResource(this.invalidResourceException, result);
break;
case IncompatibleReqs:
receiver.failBadReqs(badReqCode, vRequired, vAvailable, majorIsProblem);
break;
case NoLocalStorage:
receiver.failWithNotification(AppInstallStatus.NoLocalStorage);
break;
case BadCertificate:
receiver.failWithNotification(AppInstallStatus.BadCertificate);
break;
case DuplicateApp:
receiver.failWithNotification(AppInstallStatus.DuplicateApp);
break;
default:
receiver.failUnknown(AppInstallStatus.UnknownFailure);
break;
}
}
@Override
protected void deliverUpdate(CommCareSetupActivity receiver,
int[]... update) {
receiver.updateResourceProgress(update[0][0], update[0][1], update[0][2]);
}
@Override
protected void deliverError(CommCareSetupActivity receiver,
Exception e) {
receiver.failUnknown(AppInstallStatus.UnknownFailure);
}
};
task.connect(this);
task.executeParallel(incomingRef);
} else {
Log.i(TAG, "During install: blocked a resource install press since a task was already running");
}
}
public static CommCareApp getCommCareApp() {
ApplicationRecord newRecord =
new ApplicationRecord(PropertyUtils.genUUID().replace("-", ""),
ApplicationRecord.STATUS_UNINITIALIZED);
return new CommCareApp(newRecord);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(0, MENU_ARCHIVE, 0, Localization.get("menu.archive")).setIcon(android.R.drawable.ic_menu_upload);
menu.add(0, MENU_SMS, 1, Localization.get("menu.sms")).setIcon(android.R.drawable.stat_notify_chat);
menu.add(0, MENU_FROM_LIST, 2, Localization.get("menu.admin.install"));
return true;
}
/**
* Scan SMS messages for texts with profile references.
*
* @param installTriggeredManually if scan was triggered manually, then
* install automatically if reference is found
*/
private void performSMSInstall(boolean installTriggeredManually) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.READ_SMS)
!= PackageManager.PERMISSION_GRANTED) {
manualSMSInstall = installTriggeredManually;
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_SMS)) {
DialogCreationHelpers.buildPermissionRequestDialog(this, this,
SMS_PERMISSIONS_REQUEST,
Localization.get("permission.sms.install.title"),
Localization.get("permission.sms.install.message")).showNonPersistentDialog();
} else {
requestNeededPermissions(SMS_PERMISSIONS_REQUEST);
}
} else {
scanSMSLinks();
}
}
@Override
public void requestNeededPermissions(int requestCode) {
if (requestCode == SMS_PERMISSIONS_REQUEST) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_SMS},
requestCode);
} else {
ActivityCompat.requestPermissions(this,
Permissions.getAppPermissions(),
requestCode);
}
}
/**
* Scan the most recent incoming text messages for a message with a
* verified link to a commcare app and install it. Message scanning stops
* after the number of scanned messages reaches 'SMS_CHECK_COUNT'.
*/
private void scanSMSLinks() {
final boolean installTriggeredManually = manualSMSInstall;
RetrieveParseVerifyMessageTask<CommCareSetupActivity> smsProcessTask =
new RetrieveParseVerifyMessageTask<CommCareSetupActivity>(this, getContentResolver(), installTriggeredManually) {
@Override
protected void deliverResult(CommCareSetupActivity receiver, String result) {
if (installTriggeredManually) {
if (result != null) {
receiver.incomingRef = result;
receiver.uiState = UiState.READY_TO_INSTALL;
receiver.lastInstallMode = INSTALL_MODE_SMS;
receiver.uiStateScreenTransition();
receiver.startResourceInstall();
} else {
// only notify if this was manually triggered
receiver.displayError(Localization.get("menu.sms.not.found"));
}
} else {
if (result != null) {
receiver.incomingRef = result;
receiver.uiState = UiState.READY_TO_INSTALL;
receiver.lastInstallMode = INSTALL_MODE_SMS;
receiver.uiStateScreenTransition();
Toast.makeText(receiver, Localization.get("menu.sms.ready"), Toast.LENGTH_LONG).show();
}
}
}
@Override
protected void deliverUpdate(CommCareSetupActivity receiver, Void... update) {
}
@Override
protected void deliverError(CommCareSetupActivity receiver, Exception e) {
if (e instanceof SignatureException) {
e.printStackTrace();
receiver.fail(Localization.get("menu.sms.not.verified"));
} else if (e instanceof IOException) {
e.printStackTrace();
receiver.fail(Localization.get("menu.sms.not.retrieved"));
} else {
e.printStackTrace();
receiver.fail(Localization.get("notification.install.unknown.title"));
}
}
};
smsProcessTask.connect(this);
smsProcessTask.executeParallel();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ARCHIVE:
clearErrorMessage();
Intent i = new Intent(getApplicationContext(), InstallArchiveActivity.class);
startActivityForResult(i, OFFLINE_INSTALL);
break;
case MENU_SMS:
clearErrorMessage();
performSMSInstall(true);
break;
case MENU_FROM_LIST:
clearErrorMessage();
i = new Intent(getApplicationContext(), InstallFromListActivity.class);
startActivityForResult(i, GET_APPS_FROM_HQ);
break;
}
return true;
}
private void fail(NotificationMessage notificationMessage, boolean showAsPinnedNotifcation) {
String message;
if (showAsPinnedNotifcation) {
CommCareApplication.notificationManager().reportNotificationMessage(notificationMessage);
message = Localization.get("notification.for.details.wrapper",
new String[]{notificationMessage.getTitle()});
} else {
message = notificationMessage.getTitle();
}
fail(message);
}
/**
* Display an error and perform a UI transition
*/
private void fail(String message) {
displayError(message);
uiState = UiState.CHOOSE_INSTALL_ENTRY_METHOD;
uiStateScreenTransition();
}
/**
* Display an error without performing a UI transition
*/
private void displayError(String message) {
errorMessageToDisplay = message;
installFragment.showOrHideErrorMessage();
}
public void clearErrorMessage() {
errorMessageToDisplay = null;
}
public String getErrorMessageToDisplay() {
return errorMessageToDisplay;
}
@Override
public void reportSuccess(boolean newAppInstalled) {
CommCareApplication.notificationManager().clearNotifications("install_update");
if (newAppInstalled) {
GoogleAnalyticsUtils.reportAppInstall(lastInstallMode);
} else {
Toast.makeText(this, Localization.get("updates.success"), Toast.LENGTH_LONG).show();
}
if (Intent.ACTION_VIEW.equals(CommCareSetupActivity.this.getIntent().getAction())) {
// app installed from external action
if (getIntent().getBooleanExtra(FORCE_VALIDATE_KEY, false)) {
// force multimedia validation to ensure app shows up in multiple apps list
Intent i = new Intent(this, CommCareVerificationActivity.class);
i.putExtra(AppManagerActivity.KEY_LAUNCH_FROM_MANAGER, true);
startActivity(i);
} else {
//Call out to CommCare Home
Intent i = new Intent(getApplicationContext(), DispatchActivity.class);
startActivity(i);
}
} else {
Intent i = new Intent(getIntent());
setResult(RESULT_OK, i);
}
finish();
}
@Override
public void failMissingResource(UnresolvedResourceException ure, AppInstallStatus statusMissing) {
fail(NotificationMessageFactory.message(statusMissing, new String[]{null, ure.getResource().getDescriptor(), ure.getMessage()}), ure.isMessageUseful());
}
@Override
public void failInvalidResource(InvalidResourceException e, AppInstallStatus statusMissing) {
fail(NotificationMessageFactory.message(statusMissing, new String[]{null, e.resourceName, e.getMessage()}), true);
}
@Override
public void failBadReqs(int code, String vRequired, String vAvailable, boolean majorIsProblem) {
String versionMismatch = Localization.get("install.version.mismatch", new String[]{vRequired, vAvailable});
String error;
if (majorIsProblem) {
error = Localization.get("install.major.mismatch");
} else {
error = Localization.get("install.minor.mismatch");
}
fail(NotificationMessageFactory.message(AppInstallStatus.IncompatibleReqs, new String[]{null, versionMismatch, error}), true);
}
@Override
public void failUnknown(AppInstallStatus unknown) {
fail(NotificationMessageFactory.message(unknown), false);
}
@Override
public void updateResourceProgress(int done, int total, int phase) {
// perform safe localization because the localization dictionary might
// be the resource currently being installed.
if (!CommCareApplication.instance().isConsumerApp()) {
// Don't change the text on the progress dialog if we are showing the generic consumer
// apps startup dialog
String installProgressText =
Localization.getWithDefault("profile.found",
new String[]{"" + done, "" + total},
"Setting up app...");
updateProgress(installProgressText, DIALOG_INSTALL_PROGRESS);
}
updateProgressBar(done, total, DIALOG_INSTALL_PROGRESS);
}
@Override
public void failWithNotification(AppInstallStatus statusFailState) {
fail(NotificationMessageFactory.message(statusFailState), true);
}
@Override
public CustomProgressDialog generateProgressDialog(int taskId) {
if (taskId != DIALOG_INSTALL_PROGRESS) {
Log.w(TAG, "taskId passed to generateProgressDialog does not match "
+ "any valid possibilities in CommCareSetupActivity");
return null;
}
if (isSingleAppBuild()) {
return ConsumerAppsUtil.getGenericConsumerAppsProgressDialog(taskId, true);
} else {
return generateNormalInstallDialog(taskId);
}
}
private CustomProgressDialog generateNormalInstallDialog(int taskId) {
String title = Localization.get("updates.resources.initialization");
String message = Localization.get("updates.resources.profile");
CustomProgressDialog dialog = CustomProgressDialog.newInstance(title, message, taskId);
CustomProgressDialog lastDialog = getCurrentProgressDialog();
boolean isChecked = (lastDialog != null) && lastDialog.isChecked();
String checkboxText = Localization.get("install.keep.trying");
dialog.addCheckbox(checkboxText, isChecked);
dialog.setCancelable(false);
dialog.addProgressBar();
return dialog;
}
@Override
public void onStartInstallClicked() {
if (lastInstallMode != INSTALL_MODE_OFFLINE && isNetworkNotConnected()) {
failWithNotification(AppInstallStatus.NoConnection);
} else {
startResourceInstall();
}
}
@Override
public void onStopInstallClicked() {
incomingRef = null;
uiState = UiState.CHOOSE_INSTALL_ENTRY_METHOD;
uiStateScreenTransition();
}
public void setUiState(UiState newState) {
uiState = newState;
if (UiState.IN_URL_ENTRY.equals(uiState)) {
lastInstallMode = INSTALL_MODE_URL;
}
}
@Override
public void downloadLinkReceived(String url) {
if (url != null) {
incomingRef = url;
uiState = UiState.READY_TO_INSTALL;
uiStateScreenTransition();
Toast.makeText(this, Localization.get("menu.sms.ready"), Toast.LENGTH_LONG).show();
}
// Do not notify that url was null here because the install attempt was not user-triggered
}
@Override
public void downloadLinkReceivedAutoInstall(String url) {
if (url != null) {
incomingRef = url;
uiState = UiState.READY_TO_INSTALL;
uiStateScreenTransition();
startResourceInstall();
} else {
displayError(Localization.get("menu.sms.not.found"));
}
}
@Override
public void exceptionReceived(Exception e, boolean notify) {
String errorMsg;
if (e instanceof SignatureException) {
e.printStackTrace();
errorMsg = Localization.get("menu.sms.not.verified");
} else if (e instanceof IOException) {
e.printStackTrace();
errorMsg = Localization.get("menu.sms.not.retrieved");
} else {
e.printStackTrace();
errorMsg = Localization.get("notification.install.unknown.title");
}
if (notify && errorMsg != null) {
displayError(errorMsg);
}
}
/**
* @return Is the build configured to automatically try to install an app
* packaged up with the build without showing install options to the user.
*/
private boolean isSingleAppBuild() {
return BuildConfig.IS_SINGLE_APP_BUILD;
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == SMS_PERMISSIONS_REQUEST) {
for (int i = 0; i < permissions.length; i++) {
if (Manifest.permission.READ_SMS.equals(permissions[i]) &&
grantResults[i] == PackageManager.PERMISSION_GRANTED) {
scanSMSLinks();
}
}
} else if (requestCode == Permissions.ALL_PERMISSIONS_REQUEST) {
String[] requiredPerms = Permissions.getRequiredPerms();
for (int i = 0; i < permissions.length; i++) {
for (String requiredPerm : requiredPerms) {
if (requiredPerm.equals(permissions[i]) &&
grantResults[i] == PackageManager.PERMISSION_DENIED) {
showMissingPermissionState();
return;
}
}
}
// external storage perms were enabled, so setup temp storage,
// which fails in application setup without external storage perms.
CommCareApplication.instance().prepareTemporaryStorage();
if (!isSingleAppBuild()) {
uiState = UiState.CHOOSE_INSTALL_ENTRY_METHOD;
uiStateScreenTransition();
}
if (isSingleAppBuild()) {
SingleAppInstallation.installSingleApp(this, DIALOG_INSTALL_PROGRESS);
} else {
// Since SMS asks for more permissions, call was delayed until here
performSMSInstall(false);
}
}
}
private void showMissingPermissionState() {
if (uiState != UiState.NEEDS_PERMS) {
uiState = UiState.NEEDS_PERMS;
uiStateScreenTransition();
} else {
InstallPermissionsFragment permFragment =
(InstallPermissionsFragment)getSupportFragmentManager().findFragmentById(R.id.setup_fragment_container);
permFragment.updateDeniedState();
}
}
public static String getAnalyticsActionFromInstallMode(int installModeCode) {
switch (installModeCode) {
case INSTALL_MODE_BARCODE:
return GoogleAnalyticsFields.ACTION_BARCODE_INSTALL;
case INSTALL_MODE_OFFLINE:
return GoogleAnalyticsFields.ACTION_OFFLINE_INSTALL;
case INSTALL_MODE_SMS:
return GoogleAnalyticsFields.ACTION_SMS_INSTALL;
case INSTALL_MODE_URL:
return GoogleAnalyticsFields.ACTION_URL_INSTALL;
default:
return "";
}
}
}