Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConnectID Foundation #2847

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions app/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,27 @@
<string name="your_comment">Your comment</string>
<string name="support_email_address_default">[email protected]</string>

<string name="ConnectTokenURL">https://connectid.dimagi.com/o/token/</string>
<string name="ConnectHeartbeatURL">https://connectid.dimagi.com/users/heartbeat</string>
<string name="ConnectFetchDbKeyURL">https://connectid.dimagi.com/users/fetch_db_key</string>
<string name="ConnectChangePasswordURL">https://connectid.dimagi.com/users/change_password</string>
<string name="ConnectResetPasswordURL">https://connectid.dimagi.com/users/recover/reset_password</string>
<string name="ConnectConfirmPasswordURL">https://connectid.dimagi.com/users/recover/confirm_password</string>
<string name="ConnectSetPinURL">https://connectid.dimagi.com/users/set_recovery_pin</string>
<string name="ConnectConfirmPinURL">https://connectid.dimagi.com/users/recover/confirm_pin</string>
<string name="ConnectUpdateProfileURL">https://connectid.dimagi.com/users/update_profile</string>
<string name="ConnectChangePhoneURL">https://connectid.dimagi.com/users/change_phone</string>
<string name="ConnectPhoneAvailableURL">https://connectid.dimagi.com/users/phone_available</string>
<string name="ConnectRecoverURL">https://connectid.dimagi.com/users/recover</string>
<string name="ConnectRecoverSecondaryURL">https://connectid.dimagi.com/users/recover/secondary</string>
<string name="ConnectVerifySecondaryURL">https://connectid.dimagi.com/users/validate_secondary_phone</string>
<string name="ConnectValidatePhoneURL">https://connectid.dimagi.com/users/validate_phone</string>
<string name="ConnectRecoverConfirmOTPURL">https://connectid.dimagi.com/users/recover/confirm_otp</string>
<string name="ConnectRecoverConfirmSecondaryOTPURL">https://connectid.dimagi.com/users/recover/confirm_secondary_otp</string>
<string name="ConnectVerifyConfirmSecondaryOTPURL">https://connectid.dimagi.com/users/confirm_secondary_otp</string>
<string name="ConnectConfirmOTPURL">https://connectid.dimagi.com/users/confirm_otp</string>
<string name="ConnectRegisterURL">https://connectid.dimagi.com/users/register</string>

<!-- region: All strings for multiple apps and app-agnostic properties -->

<string name="manager_activity_name">App Manager</string>
Expand Down Expand Up @@ -384,6 +405,7 @@
<string name="recovery_forms_send_error" cc:translatable="true">Error while sending forms</string>
<string name="recovery_forms_state_unavailable" cc:translatable="true">Forms are not available. Make sure your phone storage is available</string>
<string name="recovery_network_unavailable" cc:translatable="true">No network connection. Please check your internet and try again.</string>
<string name="recovery_network_outdated" cc:translatable="true">The app is outdated and can no longer communicate with the server. Please update the app on the Google Play Store.</string>
<string name="recovery_app_manager" cc:translatable="true">Go to App Manager</string>
<string name="recovery_retry" cc:translatable="true">Retry Recovery</string>

Expand Down Expand Up @@ -456,4 +478,6 @@
<string name="fcm_default_notification_channel">notification-channel-push-notifications</string>
<string name="app_with_id_not_found">Required CommCare App is not installed on device</string>
<string name="audio_recording_notification">Audio Recording Notification</string>

<string name="connect_db_corrupt">A problem occurred with the database, please recover your account.</string>
</resources>
38 changes: 24 additions & 14 deletions app/src/org/commcare/CommCareApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
import org.commcare.utils.CommCareUtil;
import org.commcare.utils.CrashUtil;
import org.commcare.utils.DeviceIdentifier;
import org.commcare.utils.EncryptionKeyProvider;
import org.commcare.utils.FileUtil;
import org.commcare.utils.FirebaseMessagingUtil;
import org.commcare.utils.GlobalConstants;
Expand Down Expand Up @@ -202,6 +203,7 @@ public class CommCareApplication extends Application implements LifecycleEventOb

private boolean invalidateCacheOnRestore;
private CommCareNoficationManager noficationManager;
private EncryptionKeyProvider encryptionKeyProvider;

private boolean backgroundSyncSafe;

Expand Down Expand Up @@ -257,6 +259,9 @@ public void onCreate() {

FirebaseMessagingUtil.verifyToken();

//Create standard provider
setEncryptionKeyProvider(new EncryptionKeyProvider());

customiseOkHttp();

setRxJavaGlobalHandler();
Expand All @@ -279,9 +284,8 @@ protected void turnOnStrictMode() {
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
LocalePreferences.saveDeviceLocale(newConfig.locale);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why was this removed ?

}

private void initNotifications() {
Expand All @@ -300,11 +304,11 @@ private void logFirstCommCareRun() {
}
}

public void setBackgroundSyncSafe(boolean backgroundSyncSafe){
public void setBackgroundSyncSafe(boolean backgroundSyncSafe) {
this.backgroundSyncSafe = backgroundSyncSafe;
}

public boolean isBackgroundSyncSafe(){
public boolean isBackgroundSyncSafe() {
return this.backgroundSyncSafe;
}

Expand Down Expand Up @@ -345,11 +349,11 @@ private void configureCommCareEngineConstantsAndStaticRegistrations() {
// md5 hasher. Major speed improvements.
AndroidClassHasher.registerAndroidClassHashStrategy();

ActivityManager am = (ActivityManager)getSystemService(ACTIVITY_SERVICE);
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
int memoryClass = am.getMemoryClass();

PerformanceTuningUtil.updateMaxPrefetchCaseBlock(
PerformanceTuningUtil.guessLargestSupportedBulkCaseFetchSizeFromHeap(memoryClass * 1024 * 1024));
PerformanceTuningUtil.guessLargestSupportedBulkCaseFetchSizeFromHeap((long) memoryClass * 1024 * 1024));
}

public void startUserSession(byte[] symmetricKey, UserKeyRecord record, boolean restoreSession) {
Expand Down Expand Up @@ -425,12 +429,13 @@ synchronized public FirebaseAnalytics getAnalyticsInstance() {
analyticsInstance = FirebaseAnalytics.getInstance(this);
}
analyticsInstance.setUserId(getUserIdOrNull());

return analyticsInstance;
}

public int[] getCommCareVersion() {
String[] components = BuildConfig.VERSION_NAME.split("\\.");
int[] versions = new int[] {0, 0, 0};
int[] versions = new int[]{0, 0, 0};
for (int i = 0; i < components.length; i++) {
versions[i] = Integer.parseInt(components[i]);
}
Expand Down Expand Up @@ -475,7 +480,7 @@ public void initializeGlobalResources(CommCareApp app) {

@NonNull
public String getPhoneId() {
/**
/*
* https://source.android.com/devices/tech/config/device-identifiers
* https://issuetracker.google.com/issues/129583175#comment10
* Starting from Android 10, apps cannot access non-resettable device ids unless they have special career permission.
Expand Down Expand Up @@ -519,7 +524,7 @@ private void setRoots() {
private void initializeAnAppOnStartup() {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
String lastAppId = prefs.getString(LoginActivity.KEY_LAST_APP, "");
if (!"".equals(lastAppId)) {
if (!lastAppId.isEmpty()) {
ApplicationRecord lastApp = MultipleAppsUtil.getAppById(lastAppId);
if (lastApp == null || !lastApp.isUsable()) {
AppUtils.initFirstUsableAppRecord();
Expand Down Expand Up @@ -550,7 +555,6 @@ public void initializeAppResources(CommCareApp app) {
} catch (Exception e) {
Log.i("FAILURE", "Problem with loading");
Log.i("FAILURE", "E: " + e.getMessage());
e.printStackTrace();
ForceCloseLogger.reportExceptionInBg(e);
CrashUtil.reportException(e);
resourceState = STATE_CORRUPTED;
Expand Down Expand Up @@ -736,7 +740,7 @@ public void onServiceConnected(ComponentName className, IBinder service) {
synchronized (serviceLock) {
mCurrentServiceBindTimeout = MAX_BIND_TIMEOUT;

mBoundService = ((CommCareSessionService.LocalBinder)service).getService();
mBoundService = ((CommCareSessionService.LocalBinder) service).getService();
mBoundService.showLoggedInNotification(null);

// Don't let anyone touch this until it's logged in
Expand Down Expand Up @@ -922,7 +926,6 @@ public static boolean areAutomatedActionsInvalid() {

/**
* Whether the current login is a "demo" mode login.
*
* Returns a provided default value if there is no active user login
*/
public static boolean isInDemoMode(boolean defaultValue) {
Expand Down Expand Up @@ -977,8 +980,7 @@ public CommCareSessionService getSession() {
public static boolean isSessionActive() {
try {
return CommCareApplication.instance().getSession() != null;
}
catch (SessionUnavailableException e){
} catch (SessionUnavailableException e) {
return false;
}
}
Expand Down Expand Up @@ -1158,6 +1160,14 @@ public void setInvalidateCacheFlag(boolean b) {
invalidateCacheOnRestore = b;
}

public void setEncryptionKeyProvider(EncryptionKeyProvider provider) {
encryptionKeyProvider = provider;
}

public EncryptionKeyProvider getEncryptionKeyProvider() {
return encryptionKeyProvider;
}

public PrototypeFactory getPrototypeFactory(Context c) {
return AndroidPrototypeFactorySetup.getPrototypeFactory(c);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.commcare.android.database.connect.models;

import org.commcare.android.storage.framework.Persisted;
import org.commcare.models.framework.Persisting;
import org.commcare.modern.database.Table;
import org.commcare.modern.models.MetaField;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Table(ConnectAppRecord.STORAGE_KEY)
public class ConnectAppRecord extends Persisted implements Serializable {
/**
* Name of table that stores app info for Connect jobs
*/
public static final String STORAGE_KEY = "connect_apps";

public static final String META_JOB_ID = "job_id";
public static final String META_DOMAIN = "cc_domain";
public static final String META_APP_ID = "cc_app_id";
public static final String META_NAME = "name";
public static final String META_DESCRIPTION = "description";
public static final String META_ORGANIZATION = "organization";
public static final String META_PASSING_SCORE = "passing_score";
public static final String META_INSTALL_URL = "install_url";
public static final String META_MODULES = "learn_modules";

@Persisting(1)
@MetaField(META_JOB_ID)
private int jobId;
@Persisting(2)
private boolean isLearning;
@Persisting(3)
@MetaField(META_DOMAIN)
private String domain;
@Persisting(4)
@MetaField(META_APP_ID)
private String appId;
@Persisting(5)
@MetaField(META_NAME)
private String name;
@Persisting(6)
@MetaField(META_DESCRIPTION)
private String description;
@Persisting(7)
@MetaField(META_ORGANIZATION)
private String organization;

@Persisting(8)
@MetaField(META_PASSING_SCORE)
private int passingScore;
@Persisting(9)
@MetaField(META_INSTALL_URL)
private String installUrl;
@Persisting(10)
private Date lastUpdate;

private List<ConnectLearnModuleSummaryRecord> learnModules;

public ConnectAppRecord() {

}

public static ConnectAppRecord fromJson(JSONObject json, int jobId, boolean isLearning) throws JSONException {
ConnectAppRecord app = new ConnectAppRecord();

app.jobId = jobId;
app.isLearning = isLearning;

app.domain = json.has(META_DOMAIN) ? json.getString(META_DOMAIN) : "";
app.appId = json.has(META_APP_ID) ? json.getString(META_APP_ID) : "";
app.name = json.has(META_NAME) ? json.getString(META_NAME) : "";
app.description = json.has(META_DESCRIPTION) ? json.getString(META_DESCRIPTION) : "";
app.organization = json.has(META_ORGANIZATION) ? json.getString(META_ORGANIZATION) : "";
app.passingScore = json.has(META_PASSING_SCORE) && !json.isNull(META_PASSING_SCORE) ? json.getInt(META_PASSING_SCORE) : -1;
app.installUrl = json.has(META_INSTALL_URL) ? json.getString(META_INSTALL_URL) : "";

JSONArray array = json.getJSONArray(META_MODULES);
app.learnModules = new ArrayList<>();
for(int i=0; i<array.length(); i++) {
JSONObject obj = (JSONObject)array.get(i);
app.learnModules.add(ConnectLearnModuleSummaryRecord.fromJson(obj, i));
}

return app;
}

public boolean getIsLearning() { return isLearning; }
public int getJobId() { return jobId; }
public void setJobId(int jobId) { this.jobId = jobId; }

public String getAppId() { return appId; }
public String getDomain() { return domain; }
public int getPassingScore() { return passingScore; }

public List<ConnectLearnModuleSummaryRecord> getLearnModules() { return learnModules; }
public String getInstallUrl() { return installUrl; }
public void setLearnModules(List<ConnectLearnModuleSummaryRecord> modules) { learnModules = modules; }
public void setLastUpdate(Date lastUpdate) { this.lastUpdate = lastUpdate; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package org.commcare.android.database.connect.models;

import org.commcare.android.storage.framework.Persisted;
import org.commcare.connect.network.ConnectNetworkHelper;
import org.commcare.models.framework.Persisting;
import org.commcare.modern.database.Table;
import org.commcare.modern.models.MetaField;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.Serializable;
import java.text.ParseException;
import java.util.Date;

/**
* Data class for holding info related to a Connect job assessment
*
* @author dviggiano
*/
@Table(ConnectJobAssessmentRecord.STORAGE_KEY)
public class ConnectJobAssessmentRecord extends Persisted implements Serializable {
/**
* Name of database that stores Connect job assessments
*/
public static final String STORAGE_KEY = "connect_assessments";

public static final String META_JOB_ID = "id";
public static final String META_DATE = "date";
public static final String META_SCORE = "score";
public static final String META_PASSING_SCORE = "passing_score";
public static final String META_PASSED = "passed";

@Persisting(1)
@MetaField(META_JOB_ID)
private int jobId;
@Persisting(2)
@MetaField(META_DATE)
private Date date;
@Persisting(3)
@MetaField(META_SCORE)
private int score;
@Persisting(4)
@MetaField(META_PASSING_SCORE)
private int passingScore;
@Persisting(5)
@MetaField(META_PASSED)
private boolean passed;
@Persisting(6)
private Date lastUpdate;

public ConnectJobAssessmentRecord() {

}

public static ConnectJobAssessmentRecord fromJson(JSONObject json, int jobId) throws JSONException, ParseException {
ConnectJobAssessmentRecord record = new ConnectJobAssessmentRecord();

record.lastUpdate = new Date();

record.jobId = jobId;
record.date = json.has(META_DATE) ? ConnectNetworkHelper.parseDate(json.getString(META_DATE)) : new Date();
record.score = json.has(META_SCORE) ? json.getInt(META_SCORE) : -1;
record.passingScore = json.has(META_PASSING_SCORE) ? json.getInt(META_PASSING_SCORE) : -1;
record.passed = json.has(META_PASSED) && json.getBoolean(META_PASSED);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for this and rest of the api response parsing, I think we should error out if any of the mandatory fields are not present or in the wrong format (date here) . Otherwise we will bury api response errors until there is an error in user workflow and these errors would be much harder to know/surface/debug in that case. Our tolerance policy in case of api data errors should be zero tolerance and we should crash out the app in such cases. (This will need proper testing before deploy though)


return record;
}

public Date getDate() { return date; }
public int getScore() { return score; }
public int getPassingScore() { return passingScore; }

public void setLastUpdate(Date date) { lastUpdate = date; }
}
Loading
Loading