diff --git a/app/assets/locales/android_translatable_strings.txt b/app/assets/locales/android_translatable_strings.txt
index 0f4e51f014..d9ce072707 100644
--- a/app/assets/locales/android_translatable_strings.txt
+++ b/app/assets/locales/android_translatable_strings.txt
@@ -177,7 +177,7 @@ login.password=Password
login.pin.password=PIN
login.primed.prompt=Your password has been saved for you! Just press 'Log In'.
login.button=Log In
-login.app.direct=Login to your app directly.
+login.app.connect=Go to Connect Jobs
login.sync=Synchronize with server
login.bad.password=We couldn't find a user with this password. Please try another!
login.welcome.single=Welcome back! Please log in.
diff --git a/app/res/layout/connect_payment_item.xml b/app/res/layout/connect_payment_item.xml
index 801a535a17..5c0a6d3195 100644
--- a/app/res/layout/connect_payment_item.xml
+++ b/app/res/layout/connect_payment_item.xml
@@ -18,7 +18,7 @@
/>
+
+
+
+
+
+
+
diff --git a/app/res/layout/fragment_connect_progress_delivery.xml b/app/res/layout/fragment_connect_progress_delivery.xml
index 0e2425fc87..134b18dee7 100644
--- a/app/res/layout/fragment_connect_progress_delivery.xml
+++ b/app/res/layout/fragment_connect_progress_delivery.xml
@@ -51,7 +51,6 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
- android:layout_marginTop="20dp"
android:indeterminateOnly="false"
android:progressDrawable="@drawable/progress_ring"
android:rotation="-90"/>
@@ -83,7 +82,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
- android:layout_marginTop="20dp"
android:text="@string/connect_progress_warning"/>
diff --git a/app/res/layout/screen_login.xml b/app/res/layout/screen_login.xml
index 5226e3c889..2f2c7791f1 100644
--- a/app/res/layout/screen_login.xml
+++ b/app/res/layout/screen_login.xml
@@ -82,26 +82,6 @@
android:textColor="@color/cc_neutral_color"
android:textSize="@dimen/text_medium" />
-
-
-
-
-
-
https://%s/api/opportunity/%d/learn_progress
https://%s/api/opportunity/%d/claim
https://%s/api/opportunity/%d/delivery_progress
+ https://%s/api/payment/%s/confirm
https://pact.dimagi.com/keys/getkey
Your comment
@@ -485,6 +486,7 @@
ConnectID Enabled
Unlock ConnectID
+ Via ConnectID (or press here)
Unlock ConnectID first and then enter your app.
Welcome %s!
@@ -717,8 +719,25 @@
Pending Verification: %d\nNot Approved: %d\nApproved: %d
Payment Status
Earned Amount: %s\nTransferred Amount: %s
- You received money
- You received %s
+ Issued %s
+ Paid %s
+ Confirm
+ Undo confirm
+ Confirmed
+ Not Confirmed
+
+ You got paid!\n%s %s paid %s.\nHave you received the payment?
+ Yes!
+ Ask later
+ Failed to send confirmation, we will ask again later.
+
+ Login with ConnectID
+ Link to ConnectID?
+ Do you want to link this login to your ConnectID account? You will not need to type your password in the future.
+ Unlink ConnectID?
+ I see you logged in with your password although ConnectID was configured for auto-login. Would you like to sever this login from ConnectID?
+ Yes
+ No
Sign up for ConnectID
Sign out of ConnectID
diff --git a/app/src/org/commcare/activities/CommCareSetupActivity.java b/app/src/org/commcare/activities/CommCareSetupActivity.java
index 0df05b2e78..3bdefb9635 100644
--- a/app/src/org/commcare/activities/CommCareSetupActivity.java
+++ b/app/src/org/commcare/activities/CommCareSetupActivity.java
@@ -116,8 +116,7 @@ public enum UiState {
private static final int MENU_SMS = Menu.FIRST + 2;
private static final int MENU_FROM_LIST = Menu.FIRST + 3;
private static final int MENU_CONNECT_SIGN_IN = Menu.FIRST + 4;
- private static final int MENU_CONNECT_SIGN_OUT = Menu.FIRST + 5;
- private static final int MENU_CONNECT_FORGET = Menu.FIRST + 6;
+ private static final int MENU_CONNECT_FORGET = Menu.FIRST + 5;
// Activity request codes
public static final int BARCODE_CAPTURE = 1;
@@ -489,7 +488,6 @@ public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, MENU_ARCHIVE, 0, Localization.get("menu.archive")).setIcon(android.R.drawable.ic_menu_upload);
menu.add(0, MENU_FROM_LIST, 2, Localization.get("menu.app.list.install"));
menu.add(0, MENU_CONNECT_SIGN_IN, 3, getString(R.string.login_menu_connect_sign_in));
- menu.add(0, MENU_CONNECT_SIGN_OUT, 3, getString(R.string.login_menu_connect_sign_out));
menu.add(0, MENU_CONNECT_FORGET, 3, getString(R.string.login_menu_connect_forget));
return true;
}
@@ -498,7 +496,6 @@ public boolean onCreateOptionsMenu(Menu menu) {
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
menu.findItem(MENU_CONNECT_SIGN_IN).setVisible(!fromManager && !fromExternal && ConnectManager.shouldShowSignInMenuOption());
- menu.findItem(MENU_CONNECT_SIGN_OUT).setVisible(!fromManager && !fromExternal && ConnectManager.shouldShowSignOutMenuOption());
menu.findItem(MENU_CONNECT_FORGET).setVisible(!fromManager && !fromExternal && ConnectManager.shouldShowSignOutMenuOption());
return true;
@@ -623,13 +620,12 @@ public boolean onOptionsItemSelected(MenuItem item) {
startActivityForResult(i, GET_APPS_FROM_HQ);
break;
case MENU_CONNECT_SIGN_IN:
- //Exactly like pressing the Connect button, but the first time happens this way
- handleConnectButtonPress();
- break;
- case MENU_CONNECT_SIGN_OUT:
- FirebaseAnalyticsUtil.reportCccSignOut();
- ConnectManager.signOut();
- updateConnectButton();
+ //Setup ConnectID and proceed to jobs page if successful
+ ConnectManager.handleConnectButtonPress(this, success -> {
+ if(success) {
+ ConnectManager.goToConnectJobsList();
+ }
+ });
break;
case MENU_CONNECT_FORGET:
ConnectManager.forgetUser();
@@ -640,16 +636,12 @@ public boolean onOptionsItemSelected(MenuItem item) {
}
private void updateConnectButton() {
- installFragment.updateConnectButton(this, !fromManager && !fromExternal, v -> {
- handleConnectButtonPress();
- });
- }
-
- private void handleConnectButtonPress() {
- ConnectManager.handleConnectButtonPress(this, success -> {
- if(success) {
- ConnectManager.goToConnectJobsList();
- }
+ installFragment.updateConnectButton(this, !fromManager && !fromExternal && ConnectManager.isConnectIdIntroduced(), v -> {
+ ConnectManager.unlockConnect(this, success -> {
+ if(success) {
+ ConnectManager.goToConnectJobsList();
+ }
+ });
});
}
diff --git a/app/src/org/commcare/activities/DispatchActivity.java b/app/src/org/commcare/activities/DispatchActivity.java
index 8b774c9266..00cdbfde77 100644
--- a/app/src/org/commcare/activities/DispatchActivity.java
+++ b/app/src/org/commcare/activities/DispatchActivity.java
@@ -10,6 +10,7 @@
import org.commcare.AppUtils;
import org.commcare.CommCareApp;
import org.commcare.CommCareApplication;
+import org.commcare.activities.connect.ConnectManager;
import org.commcare.android.database.global.models.ApplicationRecord;
import org.commcare.android.database.user.models.SessionStateDescriptor;
import org.commcare.commcaresupportlibrary.CommCareLauncher;
@@ -203,7 +204,7 @@ private void dispatch() {
launchHomeScreen();
}
} catch (SessionUnavailableException sue) {
- if(!userTriggeredLogout || !getAppLaunchedFromConnect()) {
+ if(!userTriggeredLogout || !ConnectManager.wasAppLaunchedFromConnect(currentApp.getUniqueId())) {
launchLoginScreen();
}
else {
@@ -293,8 +294,6 @@ private void launchLoginScreen() {
i.putExtra(LoginActivity.EXTRA_APP_ID, sesssionEndpointAppID);
}
- i.putExtra(CommCareLauncher.EXTRA_FROM_CONNECT, getAppLaunchedFromConnect());
-
startActivityForResult(i, LOGIN_USER);
waitingForActivityResultFromLogin = true;
} else {
@@ -310,9 +309,6 @@ private String getSessionEndpointAppId() {
return getIntent().getStringExtra(SESSION_ENDPOINT_APP_ID);
}
- private boolean getAppLaunchedFromConnect() {
- return getIntent().getBooleanExtra(CommCareLauncher.EXTRA_FROM_CONNECT, false);
- }
private void launchHomeScreen() {
Intent i;
@@ -325,8 +321,6 @@ private void launchHomeScreen() {
i = new Intent(this, StandardHomeActivity.class);
}
i.putExtra(START_FROM_LOGIN, startFromLogin);
- i.putExtra(CommCareLauncher.EXTRA_FROM_CONNECT,
- getIntent().getBooleanExtra(CommCareLauncher.EXTRA_FROM_CONNECT, false));
i.putExtra(LoginActivity.LOGIN_MODE, lastLoginMode);
i.putExtra(LoginActivity.MANUAL_SWITCH_TO_PW_MODE, userManuallyEnteredPasswordMode);
startFromLogin = false;
diff --git a/app/src/org/commcare/activities/HomeScreenBaseActivity.java b/app/src/org/commcare/activities/HomeScreenBaseActivity.java
index 2a1f2e2d26..37e5b60e22 100644
--- a/app/src/org/commcare/activities/HomeScreenBaseActivity.java
+++ b/app/src/org/commcare/activities/HomeScreenBaseActivity.java
@@ -567,7 +567,6 @@ protected void userTriggeredLogout() {
return;
}
CommCareApplication.instance().closeUserSession();
- ConnectManager.signOut();
setResult(RESULT_OK);
finish();
}
@@ -1465,7 +1464,8 @@ private void handleDeveloperModeClicks() {
@Override
public boolean isBackEnabled() {
- if (getIntent().getBooleanExtra(CommCareLauncher.EXTRA_FROM_CONNECT, false)) {
+ String appId = CommCareApplication.instance().getCurrentApp().getUniqueId();
+ if (ConnectManager.wasAppLaunchedFromConnect(appId)) {
return true;
}
return false;
diff --git a/app/src/org/commcare/activities/LoginActivity.java b/app/src/org/commcare/activities/LoginActivity.java
index 25720b6897..e5f9dd143e 100644
--- a/app/src/org/commcare/activities/LoginActivity.java
+++ b/app/src/org/commcare/activities/LoginActivity.java
@@ -29,13 +29,10 @@
import org.commcare.CommCareApp;
import org.commcare.CommCareApplication;
-import org.commcare.activities.connect.ConnectDatabaseHelper;
import org.commcare.activities.connect.ConnectManager;
import org.commcare.android.database.app.models.UserKeyRecord;
-import org.commcare.android.database.connect.models.ConnectLinkedAppRecord;
import org.commcare.android.database.global.models.ApplicationRecord;
import org.commcare.commcaresupportlibrary.CommCareLauncher;
-import org.commcare.core.network.AuthInfo;
import org.commcare.dalvik.BuildConfig;
import org.commcare.dalvik.R;
import org.commcare.engine.resource.AppInstallStatus;
@@ -62,6 +59,7 @@
import org.commcare.views.ViewUtil;
import org.commcare.views.dialogs.CustomProgressDialog;
import org.commcare.views.dialogs.DialogCreationHelpers;
+import org.commcare.views.dialogs.StandardAlertDialog;
import org.commcare.views.notifications.MessageTag;
import org.commcare.views.notifications.NotificationActionButtonInfo;
import org.commcare.views.notifications.NotificationMessage;
@@ -87,7 +85,6 @@ public class LoginActivity extends CommCareActivity
private static final int MENU_PASSWORD_MODE = Menu.FIRST + 3;
private static final int MENU_APP_MANAGER = Menu.FIRST + 4;
private static final int MENU_CONNECT_SIGN_IN = Menu.FIRST + 5;
- private static final int MENU_CONNECT_SIGN_OUT = Menu.FIRST + 6;
private static final int MENU_CONNECT_FORGET = Menu.FIRST + 7;
public static final String NOTIFICATION_MESSAGE_LOGIN = "login_message";
@@ -131,10 +128,9 @@ protected void onCreate(Bundle savedInstanceState) {
formAndDataSyncer = new FormAndDataSyncer();
ConnectManager.init(this);
- updateConnectButton();
presetAppId = getIntent().getStringExtra(EXTRA_APP_ID);
- appLaunchedFromConnect = getIntent().getBooleanExtra(CommCareLauncher.EXTRA_FROM_CONNECT, false);
+ appLaunchedFromConnect = ConnectManager.wasAppLaunchedFromConnect(presetAppId);
if (savedInstanceState == null) {
// Only restore last user on the initial creation
@@ -203,10 +199,38 @@ protected void onSaveInstanceState(Bundle savedInstanceState) {
* upon successful login
*/
protected void initiateLoginAttempt(boolean restoreSession) {
- LoginMode loginMode = uiController.getLoginMode();
+ boolean connectJobs = isConnectJobsSelected();
- if ("".equals(uiController.getEnteredPasswordOrPin()) &&
- loginMode != LoginMode.PRIMED) {
+ if(connectJobs) {
+ ConnectManager.unlockConnect(this, success -> {
+ if(success) {
+ ConnectManager.goToConnectJobsList();
+ }
+ });
+ } else {
+ LoginMode loginMode = uiController.getLoginMode();
+
+ //See whether login is managed by ConnectID
+ String seatedAppId = CommCareApplication.instance().getCurrentApp().getUniqueId();
+ String username = uiController.getEnteredUsername();
+
+ if(!appLaunchedFromConnect && uiController.loginManagedByConnectId()) {
+ ConnectManager.unlockConnect(this, success -> {
+ if(success) {
+ String pass = ConnectManager.getStoredPasswordForApp(seatedAppId, username);
+ doLogin(loginMode, restoreSession, pass);
+ }
+ });
+ }
+ else {
+ String passwordOrPin = uiController.getEnteredPasswordOrPin();
+ doLogin(loginMode, restoreSession, passwordOrPin);
+ }
+ }
+ }
+
+ private void doLogin(LoginMode loginMode, boolean restoreSession, String passwordOrPin) {
+ if ("".equals(passwordOrPin) && loginMode != LoginMode.PRIMED) {
if (loginMode == LoginMode.PASSWORD) {
raiseLoginMessage(StockMessages.Auth_EmptyPassword, false);
} else {
@@ -219,7 +243,7 @@ protected void initiateLoginAttempt(boolean restoreSession) {
ViewUtil.hideVirtualKeyboard(LoginActivity.this);
if (loginMode == LoginMode.PASSWORD) {
- DevSessionRestorer.tryAutoLoginPasswordSave(uiController.getEnteredPasswordOrPin(), false);
+ DevSessionRestorer.tryAutoLoginPasswordSave(passwordOrPin, false);
}
if (ResourceInstallUtils.isUpdateReadyToInstall() && !UpdateActivity.isUpdateBlockedOnSync(
@@ -267,7 +291,6 @@ protected void onResume() {
// Otherwise, refresh the activity for current conditions
uiController.refreshView();
- updateConnectButton();
checkForSavedCredentials();
ConnectManager.setParent(this);
@@ -364,7 +387,6 @@ private boolean forceAutoLogin() {
private String getUniformUsername() {
String username = uiController.getEnteredUsername();
if (ConnectManager.isUnlocked() && appLaunchedFromConnect) {
- //Configure some things if we haven't already
username = ConnectManager.getUser(this).getUserId();
}
return username.toLowerCase().trim();
@@ -383,16 +405,9 @@ private boolean tryLocalLogin(String username, String passwordOrPin,
LoginMode loginMode, boolean blockRemoteKeyManagement,
DataPullMode pullModeToUse) {
try {
- if (ConnectManager.isUnlocked() && appLaunchedFromConnect) {
- //Configure some things if we haven't already
- ConnectLinkedAppRecord record = ConnectDatabaseHelper.getAppData(this,
- presetAppId, username);
- if (record == null) {
- record = ConnectManager.prepareConnectManagedApp(this, presetAppId, username);
- }
-
- passwordOrPin = record.getPassword();
- }
+ passwordOrPin = ConnectManager.checkAutoLoginAndOverridePassword(this,
+ presetAppId, username, passwordOrPin, appLaunchedFromConnect,
+ uiController.loginManagedByConnectId());
final boolean triggerMultipleUsersWarning = getMatchingUsersCount(username) > 1
&& warnMultipleAccounts;
@@ -436,8 +451,12 @@ public void dataPullCompleted() {
ViewUtil.hideVirtualKeyboard(LoginActivity.this);
CommCareApplication.notificationManager().clearNotifications(NOTIFICATION_MESSAGE_LOGIN);
- handleConnectSignIn();
+ if(handleConnectSignIn()) {
+ goToAppHomeAndFinish();
+ }
+ }
+ private void goToAppHomeAndFinish() {
Intent i = new Intent();
i.putExtra(LOGIN_MODE, uiController.getLoginMode());
i.putExtra(MANUAL_SWITCH_TO_PW_MODE, uiController.userManuallySwitchedToPasswordMode());
@@ -445,53 +464,69 @@ public void dataPullCompleted() {
finish();
}
- public void handleConnectSignIn() {
- if (!appLaunchedFromConnect) {
+ public boolean handleConnectSignIn() {
+ if (!appLaunchedFromConnect && ConnectManager.isConnectIdIntroduced()) {
String appId = CommCareApplication.instance().getCurrentApp().getUniqueId();
String username = uiController.getEnteredUsername();
String pass = uiController.getEnteredPasswordOrPin();
- ConnectManager.rememberAppCredentials(appId, username, pass);
+
+ ConnectManager.checkConnectIdLink(this, uiController.loginManagedByConnectId(), appId, username, pass, success -> {
+ goToAppHomeAndFinish();
+ });
+
+ return false;
+ }
+
+ return true;
+ }
+
+ public void handleFailedConnectSignIn() {
+ ApplicationRecord record = CommCareApplication.instance().getCurrentApp().getAppRecord();
+ if(appLaunchedFromConnect) {
+ FirebaseAnalyticsUtil.reportCccAppFailedAutoLogin(record.getApplicationId());
+ } else if(ConnectManager.isLoginManagedByConnectId(record.getUniqueId(), getUniformUsername())) {
+ //TODO: Display an additional message that the user will need to login with their password to restore CID login
+ //ConnectManager.forgetAppCredentials(record.getUniqueId(), getUniformUsername());
+ //checkForSavedCredentials();
}
}
- public void handleConnectButtonPress() {
+ public void registerConnectIdUser() {
selectedAppIndex = -1;
- ConnectManager.handleConnectButtonPress(this, success -> {
+ ConnectManager.registerUser(this, success -> {
if(success) {
ConnectManager.goToConnectJobsList();
}
});
}
- private void updateConnectButton() {
- uiController.setConnectButtonText(ConnectManager.getConnectButtonText(this));
- uiController.setConnectButtonVisible(ConnectManager.shouldShowConnectButton());
-
- uiController.updateConnectLoginState();
- }
-
private void checkForSavedCredentials() {
- if (ConnectManager.isUnlocked()) {
+ boolean loginWithConnectIDVisible = false;
+ if (ConnectManager.isConnectIdIntroduced()) {
if (appLaunchedFromConnect) {
+ loginWithConnectIDVisible = true;
uiController.setUsername("AUTO");
uiController.setPasswordOrPin("AUTO");
- uiController.setConnectButtonVisible(false);
if(!seatAppIfNeeded(presetAppId)) {
initiateLoginAttempt(uiController.isRestoreSessionChecked());
}
} else {
int selectorIndex = uiController.getSelectedAppIndex();
- if(selectorIndex >= 0) {
+ if(selectorIndex > 0) {
String selectedAppId = appIdDropdownList.size() > 0 ? appIdDropdownList.get(selectorIndex) : "";
String seatedAppId = CommCareApplication.instance().getCurrentApp().getUniqueId();
if (!uiController.isAppSelectorVisible() || selectedAppId.equals(seatedAppId)) {
- AuthInfo.ProvidedAuth credentials = ConnectManager.getCredentialsForApp(seatedAppId,
+ loginWithConnectIDVisible = ConnectManager.isLoginManagedByConnectId(seatedAppId,
uiController.getEnteredUsername());
- uiController.setPasswordOrPin(credentials != null ? credentials.password : "");
}
+ } else {
+ //Connect jobs selected from dropdown
+ loginWithConnectIDVisible = true;
}
}
}
+
+ uiController.setConnectIdLoginState(loginWithConnectIDVisible);
}
@Override
@@ -506,7 +541,6 @@ public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, MENU_PASSWORD_MODE, 1, Localization.get("login.menu.password.mode"));
menu.add(0, MENU_APP_MANAGER, 1, Localization.get("login.menu.app.manager"));
menu.add(0, MENU_CONNECT_SIGN_IN, 1, getString(R.string.login_menu_connect_sign_in));
- menu.add(0, MENU_CONNECT_SIGN_OUT, 1, getString(R.string.login_menu_connect_sign_out));
menu.add(0, MENU_CONNECT_FORGET, 1, getString(R.string.login_menu_connect_forget));
return true;
}
@@ -517,7 +551,6 @@ public boolean onPrepareOptionsMenu(Menu menu) {
menu.findItem(MENU_PERMISSIONS).setVisible(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
menu.findItem(MENU_PASSWORD_MODE).setVisible(uiController.getLoginMode() == LoginMode.PIN);
menu.findItem(MENU_CONNECT_SIGN_IN).setVisible(ConnectManager.shouldShowSignInMenuOption());
- menu.findItem(MENU_CONNECT_SIGN_OUT).setVisible(ConnectManager.shouldShowSignOutMenuOption());
menu.findItem(MENU_CONNECT_FORGET).setVisible(ConnectManager.shouldShowSignOutMenuOption());
return true;
@@ -545,20 +578,11 @@ public boolean onOptionsItemSelected(MenuItem item) {
startActivity(i);
return true;
case MENU_CONNECT_SIGN_IN:
- //Exactly like pressing the Connect button, but the first time happens this way
- handleConnectButtonPress();
- return true;
- case MENU_CONNECT_SIGN_OUT:
- FirebaseAnalyticsUtil.reportCccSignOut();
- ConnectManager.signOut();
- uiController.refreshView();
- uiController.setPasswordOrPin("");
- updateConnectButton();
+ registerConnectIdUser();
return true;
case MENU_CONNECT_FORGET:
ConnectManager.forgetUser();
uiController.setPasswordOrPin("");
- updateConnectButton();
uiController.refreshView();
return true;
default:
@@ -610,10 +634,7 @@ public void raiseMessage(NotificationMessage message, boolean showTop) {
}
uiController.setErrorMessageUi(toastText, showTop);
- if(appLaunchedFromConnect) {
- String currAppId = CommCareApplication.instance().getCurrentApp().getAppRecord().getApplicationId();
- FirebaseAnalyticsUtil.reportCccAppFailedAutoLogin(currAppId);
- }
+ handleFailedConnectSignIn();
}
/**
@@ -661,14 +682,14 @@ protected void restoreEnteredTextFromRotation() {
}
}
- protected boolean populateAppSpinner(ArrayList readyApps) {
+ protected void populateAppSpinner(ArrayList readyApps) {
ArrayList appNames = new ArrayList<>();
appIdDropdownList.clear();
- boolean includeDefault = ConnectManager.requiresUnlock();
- if (includeDefault) {
- appNames.add(Localization.get("login.app.direct"));
+ boolean includeConnect = ConnectManager.isConnectIdIntroduced();
+ if (includeConnect) {
+ appNames.add(Localization.get("login.app.connect"));
appIdDropdownList.add("");
}
@@ -682,18 +703,23 @@ protected boolean populateAppSpinner(ArrayList readyApps) {
int position = 0;
if (selectedAppIndex >= 0) {
position = selectedAppIndex;
- } else if (!includeDefault) {
+ } else if (appIdDropdownList.contains(currAppId)) {
position = appIdDropdownList.indexOf(currAppId);
}
uiController.setMultipleAppsUiState(appNames, position);
selectedAppIndex = -1;
+ }
- return includeDefault;
+ private boolean isConnectJobsSelected() {
+ return ConnectManager.isConnectIdIntroduced() && uiController.getSelectedAppIndex() == 0;
}
@Override
public void onItemSelected(AdapterView> parent, View view, int position, long id) {
- if (!ConnectManager.requiresUnlock() || position > 0) {
+ boolean selectedConnect = isConnectJobsSelected();
+ if(selectedConnect) {
+ uiController.setLoginInputsVisibility(false);
+ } else {
// Retrieve the app record corresponding to the app selected
selectedAppIndex = position;
String appId = appIdDropdownList.get(selectedAppIndex);
@@ -703,8 +729,6 @@ public void onItemSelected(AdapterView> parent, View view, int position, long
checkForSavedCredentials();
}
}
- } else {
- uiController.setLoginInputsVisibility(false);
}
}
diff --git a/app/src/org/commcare/activities/LoginActivityUiController.java b/app/src/org/commcare/activities/LoginActivityUiController.java
index fc12491325..cd64aba4de 100644
--- a/app/src/org/commcare/activities/LoginActivityUiController.java
+++ b/app/src/org/commcare/activities/LoginActivityUiController.java
@@ -20,16 +20,13 @@
import androidx.preference.PreferenceManager;
import java.util.ArrayList;
-import java.util.Locale;
import java.util.Vector;
import org.commcare.CommCareApplication;
import org.commcare.CommCareNoficationManager;
-import org.commcare.activities.connect.ConnectDatabaseHelper;
import org.commcare.activities.connect.ConnectManager;
import org.commcare.android.database.app.models.UserKeyRecord;
import org.commcare.android.database.global.models.ApplicationRecord;
-import org.commcare.core.network.AuthInfo;
import org.commcare.dalvik.R;
import org.commcare.google.services.analytics.FirebaseAnalyticsUtil;
import org.commcare.interfaces.CommCareActivityUIController;
@@ -69,12 +66,6 @@ public class LoginActivityUiController implements CommCareActivityUIController {
@UiElement(value = R.id.btn_view_notifications)
private RectangleButtonWithText notificationButton;
- @UiElement(value = R.id.connect_login_button)
- private Button connectLoginButton;
-
- @UiElement(value = R.id.login_or)
- private TextView orLabel;
-
@UiElement(value = R.id.edit_username, locale = "login.username")
private AutoCompleteTextView username;
@@ -87,7 +78,7 @@ public class LoginActivityUiController implements CommCareActivityUIController {
@UiElement(R.id.screen_login_banner_pane)
private View banner;
- @UiElement(value = R.id.login_button, locale = "login.button")
+ @UiElement(value = R.id.login_button)
private Button loginButton;
@UiElement(value = R.id.restore_session_checkbox)
@@ -96,9 +87,6 @@ public class LoginActivityUiController implements CommCareActivityUIController {
@UiElement(R.id.app_selection_spinner)
private Spinner spinner;
- @UiElement(R.id.app_label)
- private TextView appLabel;
-
@UiElement(R.id.welcome_msg)
private TextView welcomeMessage;
@@ -110,6 +98,7 @@ public class LoginActivityUiController implements CommCareActivityUIController {
private LoginMode loginMode;
private boolean manuallySwitchedToPasswordMode;
+ private boolean loginManagedByConnectId;
private final TextWatcher usernameTextWatcher = new TextWatcher() {
@Override
@@ -154,8 +143,6 @@ public void setupUI() {
setTextChangeListeners();
setBannerLayoutLogic();
- connectLoginButton.setOnClickListener(arg0 -> activity.handleConnectButtonPress());
-
loginButton.setOnClickListener(arg0 -> {
FirebaseAnalyticsUtil.reportLoginClicks();
activity.initiateLoginAttempt(isRestoreSessionChecked());
@@ -169,19 +156,17 @@ public void setupUI() {
return false;
});
+ passwordOrPin.setOnFocusChangeListener((v, hasFocus) -> {
+ if(hasFocus) {
+ setConnectIdLoginState(false);
+ }
+ });
+
notificationButton.setText(Localization.get("error.button.text"));
notificationButton.setOnClickListener(view -> CommCareNoficationManager
.performIntentCalloutToNotificationsView(activity));
}
- public void setConnectButtonText(String text) {
- connectLoginButton.setText(text);
- }
-
- public void setConnectButtonVisible(Boolean visible) {
- connectLoginButton.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
-
private void setTextChangeListeners() {
username.addTextChangedListener(usernameTextWatcher);
passwordOrPin.addTextChangedListener(passwordTextWatcher);
@@ -195,7 +180,6 @@ private void setupUsernameEntryBox() {
private void setBannerLayoutLogic() {
final View activityRootView = activity.findViewById(R.id.screen_login_main);
- final SharedPreferences prefs = CommCareApplication.instance().getCurrentApp().getAppPreferences();
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(
() -> {
int hideAll = getResources().getInteger(
@@ -225,14 +209,10 @@ public void refreshView() {
ArrayList readyApps = MultipleAppsUtil.getUsableAppRecords();
ConnectManager.filterConnectManagedApps(activity, readyApps, activity.getPresetAppId());
- boolean promptIncluded = false;
ApplicationRecord presetAppRecord = getPresetAppRecord(readyApps);
boolean noApps = readyApps.isEmpty();
- appLabel.setVisibility(noApps ? View.GONE : View.VISIBLE);
- orLabel.setVisibility(noApps || !ConnectManager.isConnectIdIntroduced() ? View.GONE : View.VISIBLE);
setLoginInputsVisibility(!noApps);
- if ((readyApps.size() == 1 && (!ConnectManager.isConnectIdIntroduced() || ConnectManager.isUnlocked()))
- || presetAppRecord != null) {
+ if (!ConnectManager.isConnectIdIntroduced() && (readyApps.size() == 1 || presetAppRecord != null)) {
setLoginInputsVisibility(true);
// Set this app as the last selected app, for use in choosing what app to initialize
@@ -240,18 +220,10 @@ public void refreshView() {
ApplicationRecord r = presetAppRecord != null ? presetAppRecord : readyApps.get(0);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(activity);
prefs.edit().putString(LoginActivity.KEY_LAST_APP, r.getUniqueId()).apply();
-
setSingleAppUiState();
-
- if (ConnectManager.isUnlocked()) {
- appLabel.setVisibility(View.VISIBLE);
- appLabel.setText(r.getDisplayName());
- }
activity.seatAppIfNeeded(r.getUniqueId());
} else {
- promptIncluded = activity.populateAppSpinner(readyApps);
- appLabel.setVisibility(View.GONE);
- spinner.setVisibility(noApps ? View.GONE : View.VISIBLE);
+ activity.populateAppSpinner(readyApps);
}
// Not using this for now, but may turn back on later
@@ -270,35 +242,14 @@ public void refreshView() {
checkEnteredUsernameForMatch();
}
- updateConnectLoginState();
-
if (!CommCareApplication.notificationManager().messagesForCommCareArePending()) {
notificationButtonView.setVisibility(View.GONE);
}
-
- if (ConnectManager.isConnectIdIntroduced()) {
- setLoginInputsVisibility(!noApps && !promptIncluded);
- }
}
public void setLoginInputsVisibility(boolean visible) {
username.setVisibility(visible ? View.VISIBLE : View.GONE);
passwordOrPin.setVisibility(visible ? View.VISIBLE : View.GONE);
- loginButton.setVisibility(visible ? View.VISIBLE : View.GONE);
- }
-
- public void updateConnectLoginState() {
- if (ConnectManager.isConnectIdIntroduced()) {
- String welcomeText;
- if (ConnectManager.isUnlocked()) {
- welcomeText = activity.getString(R.string.login_welcome_connect_signed_in,
- ConnectDatabaseHelper.getUser(activity).getName());
- } else {
- welcomeText = activity.getString(R.string.login_welcome_connect_signed_out);
- }
-
- welcomeMessage.setText(welcomeText);
- }
}
@Nullable
@@ -556,6 +507,33 @@ protected void setPasswordOrPin(String s) {
passwordOrPin.setText(s);
}
+ protected boolean loginManagedByConnectId() { return loginManagedByConnectId; }
+
+ protected void setConnectIdLoginState(boolean useConnectId) {
+ if(!useConnectId && loginManagedByConnectId) {
+ setPasswordOrPin("");
+ }
+
+ loginManagedByConnectId = useConnectId;
+
+ String text;
+ if(useConnectId) {
+ text = activity.getString(R.string.login_button_connectid);
+ }
+ else {
+ text = Localization.get("login.button");
+ }
+ loginButton.setText(text);
+
+ passwordOrPin.setBackgroundColor(getResources().getColor(useConnectId ? R.color.grey_light : R.color.white));
+ if(useConnectId) {
+ passwordOrPin.setText(R.string.login_password_by_connect);
+ passwordOrPin.clearFocus();
+ }
+
+ passwordOrPin.setInputType(useConnectId ? InputType.TYPE_CLASS_TEXT : InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ }
+
private void updateBanner() {
ImageView topBannerImageView =
banner.findViewById(R.id.main_top_banner);
diff --git a/app/src/org/commcare/activities/connect/ConnectDatabaseHelper.java b/app/src/org/commcare/activities/connect/ConnectDatabaseHelper.java
index eebd2e7353..06c4ebcb5f 100644
--- a/app/src/org/commcare/activities/connect/ConnectDatabaseHelper.java
+++ b/app/src/org/commcare/activities/connect/ConnectDatabaseHelper.java
@@ -1,13 +1,11 @@
package org.commcare.activities.connect;
-import android.app.job.JobParameters;
import android.content.Context;
import android.os.Build;
import net.sqlcipher.database.SQLiteDatabase;
import org.commcare.CommCareApplication;
-import org.commcare.adapters.ConnectJobAdapter;
import org.commcare.android.database.connect.models.ConnectAppRecord;
import org.commcare.android.database.connect.models.ConnectJobAssessmentRecord;
import org.commcare.android.database.connect.models.ConnectJobDeliveryRecord;
@@ -32,7 +30,6 @@
import java.util.Date;
import java.util.List;
import java.util.Objects;
-import java.util.Random;
import java.util.Vector;
/**
@@ -64,18 +61,6 @@ private static byte[] getConnectDbPassphrase(Context context) {
}
}
- public static String generatePassword() {
- int passwordLength = 20;
-
- String charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_!.?";
- StringBuilder password = new StringBuilder();
- for (int i = 0; i < passwordLength; i++) {
- password.append(charSet.charAt(new Random().nextInt(charSet.length())));
- }
-
- return password.toString();
- }
-
public static void init(Context context) {
synchronized (connectDbHandleLock) {
byte[] passphrase = getConnectDbPassphrase(context);
@@ -127,6 +112,8 @@ public static void forgetUser(Context context) {
getConnectStorage(context, ConnectUserRecord.class).removeAll();
}
+
+
public static ConnectLinkedAppRecord getAppData(Context context, String appId, String username) {
Vector records = getConnectStorage(context, ConnectLinkedAppRecord.class)
.getRecordsForValues(
@@ -140,17 +127,19 @@ public static void deleteAppData(Context context, ConnectLinkedAppRecord record)
storage.remove(record);
}
- public static ConnectLinkedAppRecord storeApp(Context context, String appId, String userId, String passwordOrPin, boolean workerLinked) {
+ public static ConnectLinkedAppRecord storeApp(Context context, String appId, String userId, boolean connectIdLinked, String passwordOrPin, boolean workerLinked) {
ConnectLinkedAppRecord record = getAppData(context, appId, userId);
if (record == null) {
- record = new ConnectLinkedAppRecord(appId, userId, passwordOrPin);
+ record = new ConnectLinkedAppRecord(appId, userId, connectIdLinked, passwordOrPin);
} else if (!record.getPassword().equals(passwordOrPin)) {
record.setPassword(passwordOrPin);
}
+ record.setConnectIdLinked(connectIdLinked);
+
if(workerLinked) {
//If passed in false, we'll leave the setting unchanged
- record.setWorkerLinked(workerLinked);
+ record.setWorkerLinked(true);
}
storeApp(context, record);
@@ -165,7 +154,7 @@ public static void storeApp(Context context, ConnectLinkedAppRecord record) {
public static void storeHqToken(Context context, String appId, String userId, String token, Date expiration) {
ConnectLinkedAppRecord record = getAppData(context, appId, userId);
if (record == null) {
- record = new ConnectLinkedAppRecord(appId, userId, "");
+ record = new ConnectLinkedAppRecord(appId, userId, false, "");
}
record.updateHqToken(token, expiration);
@@ -449,6 +438,11 @@ public static void storeDeliveries(Context context, List storage = getConnectStorage(context, ConnectJobPaymentRecord.class);
+ storage.write(payment);
+ }
+
public static void storePayments(Context context, List payments, int jobId, boolean pruneMissing) {
SqlStorage storage = getConnectStorage(context, ConnectJobPaymentRecord.class);
diff --git a/app/src/org/commcare/activities/connect/ConnectIdRegistrationActivity.java b/app/src/org/commcare/activities/connect/ConnectIdRegistrationActivity.java
index 6a1acedac8..733989e0ff 100644
--- a/app/src/org/commcare/activities/connect/ConnectIdRegistrationActivity.java
+++ b/app/src/org/commcare/activities/connect/ConnectIdRegistrationActivity.java
@@ -108,7 +108,7 @@ public void continuePressed() {
public void createAccount() {
uiController.setErrorText(null);
- ConnectUserRecord tempUser = new ConnectUserRecord(phone, generateUserId(), ConnectDatabaseHelper.generatePassword(),
+ ConnectUserRecord tempUser = new ConnectUserRecord(phone, generateUserId(), ConnectManager.generatePassword(),
uiController.getNameText(), "");
HashMap params = new HashMap<>();
diff --git a/app/src/org/commcare/activities/connect/ConnectManager.java b/app/src/org/commcare/activities/connect/ConnectManager.java
index 168d3a29fe..6893d0a150 100644
--- a/app/src/org/commcare/activities/connect/ConnectManager.java
+++ b/app/src/org/commcare/activities/connect/ConnectManager.java
@@ -3,6 +3,7 @@
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
+import android.widget.Toast;
import org.commcare.AppUtils;
import androidx.work.BackoffPolicy;
@@ -15,6 +16,7 @@
import org.commcare.activities.CommCareActivity;
import org.commcare.android.database.app.models.UserKeyRecord;
import org.commcare.activities.SettingsHelper;
+import org.commcare.android.database.connect.models.ConnectJobPaymentRecord;
import org.commcare.android.database.connect.models.ConnectLinkedAppRecord;
import org.commcare.android.database.connect.models.ConnectUserRecord;
import org.commcare.CommCareApplication;
@@ -33,8 +35,13 @@
import org.commcare.tasks.templates.CommCareTask;
import org.commcare.tasks.templates.CommCareTaskConnector;
import org.commcare.utils.CrashUtil;
+import org.commcare.views.dialogs.StandardAlertDialog;
import org.javarosa.core.util.PropertyUtils;
+import org.joda.time.DateTime;
+import org.joda.time.Duration;
+import java.io.IOException;
+import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Calendar;
@@ -42,6 +49,7 @@
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+import java.util.Random;
import java.util.concurrent.TimeUnit;
import javax.crypto.SecretKey;
@@ -66,7 +74,6 @@ public class ConnectManager {
public enum ConnectIdStatus {
NotIntroduced,
Registering,
- LoggedOut,
LoggedIn
}
@@ -89,6 +96,7 @@ public interface ConnectActivityCompleteListener {
private String recoverySecret = null;
private boolean forgotPassword = false;
private boolean passwordOnlyWorkflow = false;
+ private String primedAppIdForAutoLogin = null;
//Singleton, private constructor
private ConnectManager() {
@@ -110,11 +118,8 @@ public static void init(CommCareActivity> parent) {
if(manager.connectStatus == ConnectIdStatus.NotIntroduced) {
ConnectUserRecord user = ConnectDatabaseHelper.getUser(manager.parentActivity);
if (user != null) {
- if (user.getRegistrationPhase() != ConnectTask.CONNECT_NO_ACTIVITY) {
- manager.connectStatus = ConnectIdStatus.Registering;
- } else if (manager.connectStatus == ConnectIdStatus.NotIntroduced) {
- manager.connectStatus = ConnectIdStatus.LoggedOut;
- }
+ boolean registering = user.getRegistrationPhase() != ConnectTask.CONNECT_NO_ACTIVITY;
+ manager.connectStatus = registering ? ConnectIdStatus.Registering : ConnectIdStatus.LoggedIn;
}
}
}
@@ -155,14 +160,7 @@ public static boolean isConnectIdIntroduced() {
return false;
}
- return switch (getInstance().connectStatus) {
- case NotIntroduced, Registering -> false;
- case LoggedOut, LoggedIn -> true;
- };
- }
-
- public static boolean requiresUnlock() {
- return isConnectIdIntroduced() && !isUnlocked();
+ return getInstance().connectStatus == ConnectIdStatus.LoggedIn;
}
public static boolean isUnlocked() {
@@ -175,10 +173,7 @@ public static boolean shouldShowSignInMenuOption() {
return false;
}
- return switch (getInstance().connectStatus) {
- case LoggedOut, LoggedIn -> false;
- case NotIntroduced, Registering -> true;
- };
+ return getInstance().connectStatus != ConnectIdStatus.LoggedIn;
}
public static boolean shouldShowSignOutMenuOption() {
@@ -186,15 +181,12 @@ public static boolean shouldShowSignOutMenuOption() {
return false;
}
- return switch (getInstance().connectStatus) {
- case NotIntroduced, Registering, LoggedOut -> false;
- case LoggedIn -> true;
- };
+ return getInstance().connectStatus == ConnectIdStatus.LoggedIn;
}
public static String getConnectButtonText(Context context) {
return switch (getInstance().connectStatus) {
- case LoggedOut, Registering, NotIntroduced ->
+ case Registering, NotIntroduced ->
context.getString(R.string.connect_button_logged_out);
case LoggedIn -> context.getString(R.string.connect_button_logged_in);
};
@@ -205,10 +197,7 @@ public static boolean shouldShowConnectButton() {
return false;
}
- return switch (getInstance().connectStatus) {
- case NotIntroduced, Registering -> false;
- case LoggedOut, LoggedIn -> true;
- };
+ return getInstance().connectStatus == ConnectIdStatus.LoggedIn;
}
private static void completeSignin() {
@@ -223,12 +212,6 @@ private static void completeSignin() {
}
}
- public static void signOut() {
- if (getInstance().connectStatus == ConnectIdStatus.LoggedIn) {
- getInstance().connectStatus = ConnectIdStatus.LoggedOut;
- }
- }
-
public static ConnectUserRecord getUser(Context context) {
return ConnectDatabaseHelper.getUser(context);
}
@@ -255,8 +238,7 @@ public static void filterConnectManagedApps(Context context, ArrayList parent, ConnectActivityCompleteListener listener) {
+ if(manager.connectStatus == ConnectIdStatus.LoggedIn) {
+ manager.parentActivity = parent;
+ manager.loginListener = listener;
+ manager.forgotPassword = false;
+
+ ConnectUserRecord user = ConnectDatabaseHelper.getUser(manager.parentActivity);
+ manager.phase = user.shouldForcePassword() ?
+ ConnectTask.CONNECT_UNLOCK_PASSWORD :
+ ConnectTask.CONNECT_UNLOCK_BIOMETRIC;
+
+ manager.continueWorkflow();
+ }
+ }
+
+ public static void registerUser(CommCareActivity> parent, ConnectActivityCompleteListener listener) {
+ ConnectManager manager = getInstance();
+ manager.parentActivity = parent;
+ manager.loginListener = listener;
+ manager.forgotPassword = false;
+
+ ConnectTask requestCode = ConnectTask.CONNECT_NO_ACTIVITY;
+ switch (manager.connectStatus) {
+ case NotIntroduced -> {
+ requestCode = ConnectTask.CONNECT_REGISTER_OR_RECOVER_DECISION;
+ }
+ case Registering -> {
+ ConnectUserRecord user = ConnectDatabaseHelper.getUser(manager.parentActivity);
+ ConnectTask phase = user.getRegistrationPhase();
+ if (phase != ConnectTask.CONNECT_NO_ACTIVITY) {
+ requestCode = phase;
+ } else {
+ requestCode = user.shouldForcePassword() ?
+ ConnectTask.CONNECT_UNLOCK_PASSWORD :
+ ConnectTask.CONNECT_UNLOCK_BIOMETRIC;
+ }
+ }
+ default -> {
+ //Error, should never get here
+ }
+ }
+
+ if (requestCode != ConnectTask.CONNECT_NO_ACTIVITY) {
+ manager.phase = requestCode;
+ manager.continueWorkflow();
+ }
+ }
+
public static void handleConnectButtonPress(CommCareActivity> parent, ConnectActivityCompleteListener listener) {
ConnectManager manager = getInstance();
manager.parentActivity = parent;
@@ -276,7 +306,7 @@ public static void handleConnectButtonPress(CommCareActivity> parent, ConnectA
case NotIntroduced -> {
requestCode = ConnectTask.CONNECT_REGISTER_OR_RECOVER_DECISION;
}
- case LoggedOut, Registering -> {
+ case Registering -> {
ConnectUserRecord user = ConnectDatabaseHelper.getUser(manager.parentActivity);
ConnectTask phase = user.getRegistrationPhase();
if (phase != ConnectTask.CONNECT_NO_ACTIVITY) {
@@ -665,13 +695,6 @@ public static void handleFinishedActivity(int requestCode, int resultCode, Inten
}
}
- public static void rememberAppCredentials(String appId, String userId, String passwordOrPin) {
- ConnectManager manager = getInstance();
- if (isUnlocked()) {
- ConnectDatabaseHelper.storeApp(manager.parentActivity, appId, userId, passwordOrPin, false);
- }
- }
-
public static void forgetAppCredentials(String appId, String userId) {
ConnectLinkedAppRecord record = ConnectDatabaseHelper.getAppData(manager.parentActivity, appId, userId);
if (record != null) {
@@ -679,11 +702,138 @@ public static void forgetAppCredentials(String appId, String userId) {
}
}
+ public static void checkConnectIdLink(CommCareActivity> activity, boolean autoLoggedIn, String appId, String username, String password, ConnectActivityCompleteListener callback) {
+ if(isLoginManagedByConnectId(appId, username)) {
+ //ConnectID is configured
+ if(!autoLoggedIn) {
+ //See if user wishes to permanently sever the connection
+ StandardAlertDialog d = new StandardAlertDialog(activity,
+ activity.getString(R.string.login_unlink_connectid_title),
+ activity.getString(R.string.login_unlink_connectid_message));
+
+ d.setPositiveButton(activity.getString(R.string.login_link_connectid_yes), (dialog, which) -> {
+ activity.dismissAlertDialog();
+
+ unlockConnect(activity, success -> {
+ if(success) {
+ ConnectLinkedAppRecord linkedApp = ConnectDatabaseHelper.getAppData(activity, appId, username);
+ if(linkedApp != null) {
+ linkedApp.severConnectIdLink();
+ ConnectDatabaseHelper.storeApp(activity, linkedApp);
+ }
+ }
+
+ callback.connectActivityComplete(success);
+ });
+ });
+
+ d.setNegativeButton(activity.getString(R.string.login_link_connectid_no), (dialog, which) -> {
+ activity.dismissAlertDialog();
+
+ callback.connectActivityComplete(false);
+ });
+
+ activity.showAlertDialog(d);
+ return;
+ }
+ } else {
+ //ConnectID is NOT configured
+ boolean offerToLink = true;
+ boolean isSecondOffer = false;
+
+ ConnectLinkedAppRecord linkedApp = ConnectDatabaseHelper.getAppData(activity, appId, username);
+ //See if we've offered to link already
+ Date firstOffer = linkedApp != null ? linkedApp.getLinkOfferDate1() : null;
+ if(firstOffer != null) {
+ isSecondOffer = true;
+ //See if we've done the second offer
+ Date secondOffer = linkedApp.getLinkOfferDate2();
+ if(secondOffer != null) {
+ //They've declined twice, we won't bug them again
+ offerToLink = false;
+ } else {
+ //Determine whether to do second offer
+ int daysToSecondOffer = 30;
+ long millis = (new Date()).getTime() - firstOffer.getTime();
+ long days = TimeUnit.DAYS.convert(millis, TimeUnit.MILLISECONDS);
+ offerToLink = days >= daysToSecondOffer;
+ }
+ }
+
+ if(offerToLink) {
+ if(linkedApp == null) {
+ //Create the linked app record (even if just to remember that we offered
+ linkedApp = ConnectDatabaseHelper.storeApp(activity, appId, username, false, "", false);
+ }
+
+ //Update that we offered
+ if(isSecondOffer) {
+ linkedApp.setLinkOfferDate2(new Date());
+ } else {
+ linkedApp.setLinkOfferDate1(new Date());
+ }
+
+ final ConnectLinkedAppRecord appRecordFinal = linkedApp;
+ StandardAlertDialog d = new StandardAlertDialog(activity,
+ activity.getString(R.string.login_link_connectid_title),
+ activity.getString(R.string.login_link_connectid_message));
+
+ d.setPositiveButton(activity.getString(R.string.login_link_connectid_yes), (dialog, which) -> {
+ activity.dismissAlertDialog();
+
+ unlockConnect(activity, success -> {
+ if(success) {
+ appRecordFinal.linkToConnectId(password);
+ ConnectDatabaseHelper.storeApp(activity, appRecordFinal);
+
+ //Link the HQ user by aqcuiring the SSO token for the first time
+ ConnectSsoHelper.retrieveHqSsoTokenAsync(activity, username, true, auth -> {
+ if(auth == null) {
+ Toast.makeText(activity, "Failed to acquire SSO token", Toast.LENGTH_SHORT).show();
+ //TODO: Re-enable when token working again
+ //ConnectManager.forgetAppCredentials(appId, username);
+ }
+
+ callback.connectActivityComplete(true);
+ });
+ } else {
+ callback.connectActivityComplete(false);
+ }
+ });
+ });
+
+ d.setNegativeButton(activity.getString(R.string.login_link_connectid_no), (dialog, which) -> {
+ activity.dismissAlertDialog();
+
+ //Save updated record indicating that we offered
+ ConnectDatabaseHelper.storeApp(activity, appRecordFinal);
+
+ callback.connectActivityComplete(false);
+ });
+
+ activity.showAlertDialog(d);
+ return;
+ }
+ }
+
+ callback.connectActivityComplete(false);
+ }
+
+ public static boolean isLoginManagedByConnectId(String appId, String userId) {
+ AuthInfo.ProvidedAuth auth = getCredentialsForApp(appId, userId);
+ return auth != null;
+ }
+
+ public static String getStoredPasswordForApp(String appId, String userId) {
+ AuthInfo.ProvidedAuth auth = getCredentialsForApp(appId, userId);
+ return auth != null ? auth.password : null;
+ }
+
@Nullable
public static AuthInfo.ProvidedAuth getCredentialsForApp(String appId, String userId) {
ConnectLinkedAppRecord record = ConnectDatabaseHelper.getAppData(manager.parentActivity, appId,
userId);
- if (record != null && record.getPassword().length() > 0) {
+ if (record != null && record.getConnectIdLinked() && record.getPassword().length() > 0) {
return new AuthInfo.ProvidedAuth(record.getUserId(), record.getPassword(), false);
}
@@ -784,15 +934,43 @@ public static void launchApp(Context context, boolean isLearning, String appId)
String appType = isLearning ? "Learn" : "Deliver";
FirebaseAnalyticsUtil.reportCccAppLaunch(appType, appId);
- CommCareLauncher.launchCommCareForAppIdFromConnect(context, appId);
+ getInstance().primedAppIdForAutoLogin = appId;
+
+ CommCareLauncher.launchCommCareForAppId(context, appId);
+ }
+
+ public static boolean wasAppLaunchedFromConnect(String appId) {
+ String primed = getInstance().primedAppIdForAutoLogin;
+ return primed != null && primed.equals(appId);
+ }
+
+ public static String checkAutoLoginAndOverridePassword(Context context, String appId, String username,
+ String passwordOrPin, boolean appLaunchedFromConnect, boolean uiInAutoLogin) {
+ if (isUnlocked()) {
+ if(appLaunchedFromConnect) {
+ //Configure some things if we haven't already
+ ConnectLinkedAppRecord record = ConnectDatabaseHelper.getAppData(context,
+ appId, username);
+ if (record == null) {
+ record = prepareConnectManagedApp(context, appId, username);
+ }
+
+ passwordOrPin = record.getPassword();
+ } else if(uiInAutoLogin) {
+ String seatedAppId = CommCareApplication.instance().getCurrentApp().getUniqueId();
+ passwordOrPin = ConnectManager.getStoredPasswordForApp(seatedAppId, username);
+ }
+ }
+
+ return passwordOrPin;
}
public static ConnectLinkedAppRecord prepareConnectManagedApp(Context context, String appId, String username) {
- //Ctreate password
- String password = ConnectDatabaseHelper.generatePassword();
+ //Create app password
+ String password = generatePassword();
//Store ConnectLinkedAppRecord (note worker already linked)
- ConnectLinkedAppRecord appRecord = ConnectDatabaseHelper.storeApp(context, appId, username, password, true);
+ ConnectLinkedAppRecord appRecord = ConnectDatabaseHelper.storeApp(context, appId, username, true, password, true);
//Store UKR
SecretKey newKey = CryptUtil.generateSemiRandomKey();
@@ -817,4 +995,41 @@ public static ConnectLinkedAppRecord prepareConnectManagedApp(Context context, S
return appRecord;
}
+
+ public static void updatePaymentConfirmed(Context context, final ConnectJobPaymentRecord payment, boolean confirmed, ConnectActivityCompleteListener listener) {
+ ConnectNetworkHelper.setPaymentConfirmed(context, payment.getPaymentId(), confirmed, new ConnectNetworkHelper.INetworkResultHandler() {
+ @Override
+ public void processSuccess(int responseCode, InputStream responseData) {
+ payment.setConfirmed(confirmed);
+ ConnectDatabaseHelper.storePayment(context, payment);
+
+ //No need to report to user
+ listener.connectActivityComplete(true);
+ }
+
+ @Override
+ public void processFailure(int responseCode, IOException e) {
+ Toast.makeText(context, R.string.connect_payment_confirm_failed, Toast.LENGTH_SHORT).show();
+ listener.connectActivityComplete(false);
+ }
+
+ @Override
+ public void processNetworkFailure() {
+ Toast.makeText(context, R.string.connect_payment_confirm_failed, Toast.LENGTH_SHORT).show();
+ listener.connectActivityComplete(false);
+ }
+ });
+ }
+
+ public static String generatePassword() {
+ int passwordLength = 20;
+
+ String charSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_!.?";
+ StringBuilder password = new StringBuilder();
+ for (int i = 0; i < passwordLength; i++) {
+ password.append(charSet.charAt(new Random().nextInt(charSet.length())));
+ }
+
+ return password.toString();
+ }
}
diff --git a/app/src/org/commcare/activities/connect/ConnectNetworkHelper.java b/app/src/org/commcare/activities/connect/ConnectNetworkHelper.java
index 91d992d1e6..a837221f04 100644
--- a/app/src/org/commcare/activities/connect/ConnectNetworkHelper.java
+++ b/app/src/org/commcare/activities/connect/ConnectNetworkHelper.java
@@ -1,6 +1,11 @@
package org.commcare.activities.connect;
import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkInfo;
+import android.os.Build;
import android.os.Handler;
import com.google.common.collect.ArrayListMultimap;
@@ -82,6 +87,22 @@ private static ConnectNetworkHelper getInstance() {
return Loader.INSTANCE;
}
+ public static boolean isOnline(Context context) {
+ ConnectivityManager manager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ Network network = manager.getActiveNetwork();
+ if(network == null) {
+ return false;
+ }
+
+ NetworkCapabilities capabilities = manager.getNetworkCapabilities(network);
+ return capabilities != null && capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) || capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR);
+ } else {
+ NetworkInfo info = manager.getActiveNetworkInfo();
+ return info != null && info.isConnected();
+ }
+ }
+
public static PostResult postSync(Context context, String url, AuthInfo authInfo,
HashMap params, boolean useFormEncoding) {
return getInstance().postSyncInternal(context, url, authInfo, params, useFormEncoding);
@@ -101,56 +122,61 @@ public static boolean get(Context context, String url, AuthInfo authInfo,
private PostResult postSyncInternal(Context context, String url, AuthInfo authInfo,
HashMap params, boolean useFormEncoding) {
isBusy = true;
- showProgressDialog(context);
- HashMap headers = new HashMap<>();
- RequestBody requestBody;
+ try {
+ showProgressDialog(context);
+ HashMap headers = new HashMap<>();
+ RequestBody requestBody;
+
+ if (useFormEncoding) {
+ Multimap multimap = ArrayListMultimap.create();
+ for (Map.Entry entry : params.entrySet()) {
+ multimap.put(entry.getKey(), entry.getValue());
+ }
- if (useFormEncoding) {
- Multimap multimap = ArrayListMultimap.create();
- for (Map.Entry entry : params.entrySet()) {
- multimap.put(entry.getKey(), entry.getValue());
+ requestBody = ModernHttpRequester.getPostBody(multimap);
+ headers = getContentHeadersForXFormPost(requestBody);
+ } else {
+ Gson gson = new Gson();
+ String json = gson.toJson(params);
+ requestBody = RequestBody.create(MediaType.parse("application/json"), json);
}
- requestBody = ModernHttpRequester.getPostBody(multimap);
- headers = getContentHeadersForXFormPost(requestBody);
- } else {
- Gson gson = new Gson();
- String json = gson.toJson(params);
- requestBody = RequestBody.create(MediaType.parse("application/json"), json);
- }
-
- ModernHttpRequester requester = CommCareApplication.instance().buildHttpRequester(
- context,
- url,
- ImmutableMultimap.of(),
- headers,
- requestBody,
- null,
- HTTPMethod.POST,
- authInfo,
- null,
- false);
-
- int responseCode = -1;
- InputStream stream = null;
- IOException exception = null;
- try {
- Response response = requester.makeRequest();
- responseCode = response.code();
- if (response.isSuccessful()) {
- stream = requester.getResponseStream(response);
- }
- else if(response.errorBody() != null) {
- String error = response.errorBody().string();
- Logger.log("DAVE", error);
+ ModernHttpRequester requester = CommCareApplication.instance().buildHttpRequester(
+ context,
+ url,
+ ImmutableMultimap.of(),
+ headers,
+ requestBody,
+ null,
+ HTTPMethod.POST,
+ authInfo,
+ null,
+ false);
+
+ int responseCode = -1;
+ InputStream stream = null;
+ IOException exception = null;
+ try {
+ Response response = requester.makeRequest();
+ responseCode = response.code();
+ if (response.isSuccessful()) {
+ stream = requester.getResponseStream(response);
+ } else if (response.errorBody() != null) {
+ String error = response.errorBody().string();
+ Logger.log("DAVE", error);
+ }
+ } catch (IOException e) {
+ exception = e;
}
- } catch (IOException e) {
- exception = e;
- }
- onFinishProcessing(context);
+ onFinishProcessing(context);
- return new PostResult(responseCode, stream, exception);
+ return new PostResult(responseCode, stream, exception);
+ }
+ catch(Exception e) {
+ isBusy = false;
+ return new PostResult(-1, null, null);
+ }
}
private boolean postInternal(Context context, String url, AuthInfo authInfo,
@@ -508,4 +534,25 @@ public static boolean getDeliveries(Context context, int jobId, INetworkResultHa
return true;
}
+
+ public static boolean setPaymentConfirmed(Context context, String paymentId, boolean confirmed, INetworkResultHandler handler) {
+ if (getInstance().isBusy) {
+ return false;
+ }
+
+ ConnectSsoHelper.retrieveConnectTokenAsync(context, token -> {
+ if(token == null) {
+ return;
+ }
+
+ String url = context.getString(R.string.ConnectPaymentConfirmationURL, BuildConfig.CCC_HOST, paymentId);
+
+ HashMap params = new HashMap<>();
+ params.put("confirmed", confirmed ? "true" : "false");
+
+ getInstance().postInternal(context, url, token, params, true, handler);
+ });
+
+ return true;
+ }
}
diff --git a/app/src/org/commcare/activities/connect/ConnectSsoHelper.java b/app/src/org/commcare/activities/connect/ConnectSsoHelper.java
index e21c771082..8b21f4903f 100644
--- a/app/src/org/commcare/activities/connect/ConnectSsoHelper.java
+++ b/app/src/org/commcare/activities/connect/ConnectSsoHelper.java
@@ -32,19 +32,48 @@ public interface TokenCallback {
void tokenRetrieved(AuthInfo.TokenAuth token);
}
- public static AuthInfo.TokenAuth acquireSsoTokenSync(Context context, String hqUsername) {
+ //Used for aynchronously retrieving HQ or SSO token
+ private static class TokenTask extends AsyncTask {
+ private final WeakReference weakContext;
+ private final String hqUsername; //null for Connect
+ private final boolean linkHqUser;
+ TokenCallback callback;
+ TokenTask(Context context, String hqUsername, boolean linkHqUser, TokenCallback callback) {
+ super();
+ this.weakContext = new WeakReference<>(context);
+ this.hqUsername = hqUsername;
+ this.linkHqUser = linkHqUser;
+ this.callback = callback;
+ }
+
+ @Override
+ protected AuthInfo.TokenAuth doInBackground(Void... voids) {
+ Context context = weakContext.get();
+ if(hqUsername == null) {
+ return retrieveConnectTokenSync(context);
+ }
+
+ return retrieveHqSsoTokenSync(context, hqUsername, linkHqUser);
+ }
+
+ @Override
+ protected void onPostExecute(AuthInfo.TokenAuth token) {
+ callback.tokenRetrieved(token);
+ }
+ }
+
+ public static void retrieveHqSsoTokenAsync(Context context, String hqUsername, boolean linkHqUser, TokenCallback callback) {
+ TokenTask task = new TokenTask(context, hqUsername, linkHqUser, callback);
+
+ task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ public static AuthInfo.TokenAuth retrieveHqSsoTokenSync(Context context, String hqUsername, boolean performLink) {
if (!ConnectManager.isUnlocked()) {
return null;
}
String seatedAppId = CommCareApplication.instance().getCurrentApp().getUniqueId();
-// String hqUser;
-// try {
-// hqUser = CommCareApplication.instance().getRecordForCurrentUser().getUsername();
-// } catch (Exception e) {
-// //No token if no session
-// return null;
-// }
ConnectLinkedAppRecord appRecord = ConnectDatabaseHelper.getAppData(context, seatedAppId, hqUsername);
if (appRecord == null) {
@@ -53,53 +82,32 @@ public static AuthInfo.TokenAuth acquireSsoTokenSync(Context context, String hqU
//See if we already have a valid token
AuthInfo.TokenAuth hqTokenAuth = ConnectManager.getTokenCredentialsForApp(seatedAppId, hqUsername);
- if (hqTokenAuth == null) {
+ if (hqTokenAuth == null && (performLink || appRecord.getWorkerLinked())) {
//First get a valid Connect token
- AuthInfo.TokenAuth connectToken = retrieveConnectToken(context);
-
- if (connectToken == null) {
- //If we can't get a valid Connect token there's no point continuing
- return null;
- }
+ AuthInfo.TokenAuth connectToken = retrieveConnectTokenSync(context);
- //Link user if necessary
- linkHqWorker(context, hqUsername, appRecord.getPassword(), connectToken.bearerToken);
+ //If we can't get a valid Connect token there's no point continuing
+ if (connectToken != null) {
+ if(!appRecord.getWorkerLinked()) {
+ //Link user if necessary
+ linkHqWorker(context, hqUsername, appRecord.getPassword(), connectToken.bearerToken);
+ }
- //Retrieve HQ token
- hqTokenAuth = retrieveHqToken(context, hqUsername, connectToken.bearerToken);
+ //Retrieve HQ token
+ hqTokenAuth = retrieveHqTokenApi(context, hqUsername, connectToken.bearerToken);
+ }
}
return hqTokenAuth;
}
- private static class TokenTask extends AsyncTask {
- private final WeakReference weakContext;
- TokenCallback callback;
- TokenTask(Context context, TokenCallback callback) {
- super();
- weakContext = new WeakReference<>(context);
- this.callback = callback;
- }
-
- @Override
- protected AuthInfo.TokenAuth doInBackground(Void... voids) {
- Context context = weakContext.get();
- return retrieveConnectToken(context);
- }
-
- @Override
- protected void onPostExecute(AuthInfo.TokenAuth token) {
- callback.tokenRetrieved(token);
- }
- }
-
public static void retrieveConnectTokenAsync(Context context, TokenCallback callback) {
- TokenTask task = new TokenTask(context, callback);
+ TokenTask task = new TokenTask(context, null, false, callback);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
- public static AuthInfo.TokenAuth retrieveConnectToken(Context context) {
+ public static AuthInfo.TokenAuth retrieveConnectTokenSync(Context context) {
AuthInfo.TokenAuth connectToken = ConnectManager.getConnectToken();
if(connectToken != null) {
return connectToken;
@@ -170,7 +178,7 @@ private static void linkHqWorker(Context context, String hqUsername, String hqPa
}
}
- private static AuthInfo.TokenAuth retrieveHqToken(Context context, String hqUsername, String connectToken) {
+ private static AuthInfo.TokenAuth retrieveHqTokenApi(Context context, String hqUsername, String connectToken) {
HashMap params = new HashMap<>();
params.put("client_id", "4eHlQad1oasGZF0lPiycZIjyL0SY1zx7ZblA6SCV");
params.put("scope", "mobile_access sync");
diff --git a/app/src/org/commcare/android/database/connect/models/ConnectJobPaymentRecord.java b/app/src/org/commcare/android/database/connect/models/ConnectJobPaymentRecord.java
index 681943c367..2eca573b02 100644
--- a/app/src/org/commcare/android/database/connect/models/ConnectJobPaymentRecord.java
+++ b/app/src/org/commcare/android/database/connect/models/ConnectJobPaymentRecord.java
@@ -4,16 +4,15 @@
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.text.ParseException;
import java.text.SimpleDateFormat;
-import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
+import java.util.concurrent.TimeUnit;
@Table(ConnectJobPaymentRecord.STORAGE_KEY)
public class ConnectJobPaymentRecord extends Persisted implements Serializable {
@@ -25,6 +24,9 @@ public class ConnectJobPaymentRecord extends Persisted implements Serializable {
public static final String META_JOB_ID = "job_id";
public static final String META_AMOUNT = "amount";
public static final String META_DATE = "date_paid";
+ public static final String META_PAYMENT_ID = "payment_id";
+ public static final String META_CONFIRMED = "confirmed";
+ public static final String META_CONFIRMED_DATE = "date_confirmed";
@Persisting(1)
@MetaField(META_JOB_ID)
@@ -38,8 +40,33 @@ public class ConnectJobPaymentRecord extends Persisted implements Serializable {
@MetaField(META_AMOUNT)
private String amount;
+ @Persisting(4)
+ @MetaField(META_PAYMENT_ID)
+ private String paymentId;
+ @Persisting(5)
+ @MetaField(META_CONFIRMED)
+ private boolean confirmed;
+
+ @Persisting(6)
+ @MetaField(META_CONFIRMED_DATE)
+ private Date confirmedDate;
+
public ConnectJobPaymentRecord() {}
+ public static ConnectJobPaymentRecord fromV3(ConnectJobPaymentRecordV3 oldRecord) {
+ ConnectJobPaymentRecord newRecord = new ConnectJobPaymentRecord();
+
+ newRecord.jobId = oldRecord.getJobId();
+ newRecord.date = oldRecord.getDate();
+ newRecord.amount = oldRecord.getAmount();
+
+ newRecord.paymentId = "-1";
+ newRecord.confirmed = false;
+ newRecord.confirmedDate = new Date();
+
+ return newRecord;
+ }
+
public static ConnectJobPaymentRecord fromJson(JSONObject json, int jobId) throws JSONException, ParseException {
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
@@ -49,10 +76,45 @@ public static ConnectJobPaymentRecord fromJson(JSONObject json, int jobId) throw
payment.date = json.has(META_DATE) ? df.parse(json.getString(META_DATE)) : new Date();
payment.amount = String.format(Locale.ENGLISH, "%d", json.has(META_AMOUNT) ? json.getInt(META_AMOUNT) : 0);
+ payment.paymentId = json.has("id") ? json.getString("id") : "";
+ payment.confirmed = json.has(META_CONFIRMED) && json.getBoolean(META_CONFIRMED);
+ payment.confirmedDate = json.has(META_CONFIRMED_DATE) && !json.isNull(META_CONFIRMED_DATE) ? df.parse(json.getString(META_CONFIRMED_DATE)) : new Date();
+
return payment;
}
+ public String getPaymentId() {return paymentId; }
public Date getDate() { return date;}
public String getAmount() { return amount; }
+
+ public boolean getConfirmed() {return confirmed; }
+ public Date getConfirmedDate() {return confirmedDate; }
+
+ public void setConfirmed(boolean confirmed) {
+ this.confirmed = confirmed;
+ if(confirmed) {
+ confirmedDate = new Date();
+ }
+ }
+
+ public boolean allowConfirm() {
+ if (confirmed) {
+ return false;
+ }
+
+ long millis = (new Date()).getTime() - date.getTime();
+ long days = TimeUnit.DAYS.convert(millis, TimeUnit.MILLISECONDS);
+ return days < 7;
+ }
+
+ public boolean allowConfirmUndo() {
+ if (!confirmed) {
+ return false;
+ }
+
+ long millis = (new Date()).getTime() - confirmedDate.getTime();
+ long days = TimeUnit.DAYS.convert(millis, TimeUnit.MILLISECONDS);
+ return days < 1;
+ }
}
diff --git a/app/src/org/commcare/android/database/connect/models/ConnectJobPaymentRecordV3.java b/app/src/org/commcare/android/database/connect/models/ConnectJobPaymentRecordV3.java
new file mode 100644
index 0000000000..5eaac949ad
--- /dev/null
+++ b/app/src/org/commcare/android/database/connect/models/ConnectJobPaymentRecordV3.java
@@ -0,0 +1,56 @@
+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.JSONException;
+import org.json.JSONObject;
+
+import java.io.Serializable;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+@Table(ConnectJobPaymentRecordV3.STORAGE_KEY)
+public class ConnectJobPaymentRecordV3 extends Persisted implements Serializable {
+ /**
+ * Name of database that stores app info for Connect jobs
+ */
+ public static final String STORAGE_KEY = "connect_payments";
+
+ public static final String META_JOB_ID = "job_id";
+ public static final String META_AMOUNT = "amount";
+ public static final String META_DATE = "date_paid";
+
+ @Persisting(1)
+ @MetaField(META_JOB_ID)
+ private int jobId;
+ @Persisting(2)
+ @MetaField(META_DATE)
+ private Date date;
+ @Persisting(3)
+ @MetaField(META_AMOUNT)
+ private String amount;
+
+ public ConnectJobPaymentRecordV3() {}
+
+ public static ConnectJobPaymentRecordV3 fromJson(JSONObject json, int jobId) throws JSONException, ParseException {
+ SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault());
+
+ ConnectJobPaymentRecordV3 payment = new ConnectJobPaymentRecordV3();
+
+ payment.jobId = jobId;
+ payment.date = json.has(META_DATE) ? df.parse(json.getString(META_DATE)) : new Date();
+ payment.amount = String.format(Locale.ENGLISH, "%d", json.has(META_AMOUNT) ? json.getInt(META_AMOUNT) : 0);
+
+ return payment;
+ }
+
+ public int getJobId() { return jobId; }
+
+ public Date getDate() { return date;}
+
+ public String getAmount() { return amount; }
+}
diff --git a/app/src/org/commcare/android/database/connect/models/ConnectJobRecord.java b/app/src/org/commcare/android/database/connect/models/ConnectJobRecord.java
index 13d4eb4c2b..434174e61e 100644
--- a/app/src/org/commcare/android/database/connect/models/ConnectJobRecord.java
+++ b/app/src/org/commcare/android/database/connect/models/ConnectJobRecord.java
@@ -162,7 +162,7 @@ public static ConnectJobRecord fromJson(JSONObject json) throws JSONException, P
job.lastLearnUpdate = new Date();
job.lastDeliveryUpdate = new Date();
- job.jobId = json.has(META_JOB_ID) ? json.getInt(META_JOB_ID) : -1;
+ job.jobId = json.getInt(META_JOB_ID);
job.title = json.has(META_NAME) ? json.getString(META_NAME) : "";
job.description = json.has(META_DESCRIPTION) ? json.getString(META_DESCRIPTION) : "";
job.organization = json.has(META_ORGANIZATION) ? json.getString(META_ORGANIZATION) : "";
diff --git a/app/src/org/commcare/android/database/connect/models/ConnectLinkedAppRecord.java b/app/src/org/commcare/android/database/connect/models/ConnectLinkedAppRecord.java
index 0aa3f1c3e8..2f93a30001 100644
--- a/app/src/org/commcare/android/database/connect/models/ConnectLinkedAppRecord.java
+++ b/app/src/org/commcare/android/database/connect/models/ConnectLinkedAppRecord.java
@@ -21,33 +21,69 @@ public class ConnectLinkedAppRecord extends Persisted {
public static final String META_APP_ID = "app_id";
public static final String META_USER_ID = "user_id";
+ public static final String META_CONNECTID_LINKED = "connectid_linked";
+ public static final String META_OFFERED_1 = "link_offered_1";
+ public static final String META_OFFERED_1_DATE = "link_offered_1_date";
+ public static final String META_OFFERED_2 = "link_offered_2";
+ public static final String META_OFFERED_2_DATE = "link_offered_2_date";
@Persisting(1)
@MetaField(META_APP_ID)
private String appId;
-
@Persisting(2)
@MetaField(META_USER_ID)
private String userId;
-
@Persisting(3)
private String password;
-
@Persisting(4)
private boolean workerLinked;
@Persisting(value = 5, nullable = true)
private String hqToken;
-
@Persisting(6)
private Date hqTokenExpiration;
+ @Persisting(7)
+ @MetaField(META_CONNECTID_LINKED)
+ private boolean connectIdLinked;
+ @Persisting(8)
+ @MetaField(META_OFFERED_1)
+ private boolean linkOffered1;
+ @Persisting(9)
+ @MetaField(META_OFFERED_1_DATE)
+ private Date linkOfferDate1;
+ @Persisting(10)
+ @MetaField(META_OFFERED_2)
+ private boolean linkOffered2;
+ @Persisting(11)
+ @MetaField(META_OFFERED_2_DATE)
+ private Date linkOfferDate2;
public ConnectLinkedAppRecord() {
hqTokenExpiration = new Date();
}
- public ConnectLinkedAppRecord(String appId, String userId, String password) {
+ public static ConnectLinkedAppRecord fromV3(ConnectLinkedAppRecordV3 oldRecord) {
+ ConnectLinkedAppRecord newRecord = new ConnectLinkedAppRecord();
+
+ newRecord.appId = oldRecord.getAppId();
+ newRecord.userId = oldRecord.getUserId();
+ newRecord.password = oldRecord.getPassword();
+ newRecord.workerLinked = oldRecord.getWorkerLinked();
+ newRecord.hqToken = oldRecord.getHqToken();
+ newRecord.hqTokenExpiration = oldRecord.getHqTokenExpiration();
+
+ newRecord.connectIdLinked = true;
+ newRecord.linkOffered1 = true;
+ newRecord.linkOfferDate1 = new Date();
+ newRecord.linkOffered2 = false;
+ newRecord.linkOfferDate2 = new Date();
+
+ return newRecord;
+ }
+
+ public ConnectLinkedAppRecord(String appId, String userId, boolean connectIdLinked, String password) {
this.appId = appId;
this.userId = userId;
+ this.connectIdLinked = connectIdLinked;
this.password = password;
hqTokenExpiration = new Date();
@@ -85,4 +121,33 @@ public void updateHqToken(String token, Date expirationDate) {
hqToken = token;
hqTokenExpiration = expirationDate;
}
+
+ public boolean getConnectIdLinked() { return connectIdLinked; }
+ public void setConnectIdLinked(boolean linked) { connectIdLinked = linked; }
+
+ public void linkToConnectId(String password) {
+ connectIdLinked = true;
+ this.password = password;
+ }
+
+ public void severConnectIdLink() {
+ connectIdLinked = false;
+ password = "";
+ }
+
+ public Date getLinkOfferDate1() {
+ return linkOffered1 ? linkOfferDate1 : null;
+ }
+ public void setLinkOfferDate1(Date date) {
+ linkOffered1 = true;
+ linkOfferDate1 = date;
+ }
+
+ public Date getLinkOfferDate2() {
+ return linkOffered2 ? linkOfferDate2 : null;
+ }
+ public void setLinkOfferDate2(Date date) {
+ linkOffered2 = true;
+ linkOfferDate2 = date;
+ }
}
diff --git a/app/src/org/commcare/android/database/connect/models/ConnectLinkedAppRecordV3.java b/app/src/org/commcare/android/database/connect/models/ConnectLinkedAppRecordV3.java
new file mode 100644
index 0000000000..85ae191705
--- /dev/null
+++ b/app/src/org/commcare/android/database/connect/models/ConnectLinkedAppRecordV3.java
@@ -0,0 +1,85 @@
+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 java.util.Date;
+
+/**
+ * DB model holding info for an HQ app linked to ConnectID
+ *
+ * @author dviggiano
+ */
+@Table(ConnectLinkedAppRecordV3.STORAGE_KEY)
+public class ConnectLinkedAppRecordV3 extends Persisted {
+ /**
+ * Name of database that stores Connect user records
+ */
+ public static final String STORAGE_KEY = "app_info";
+
+ public static final String META_APP_ID = "app_id";
+ public static final String META_USER_ID = "user_id";
+
+ @Persisting(1)
+ @MetaField(META_APP_ID)
+ private String appId;
+ @Persisting(2)
+ @MetaField(META_USER_ID)
+ private String userId;
+ @Persisting(3)
+ private String password;
+ @Persisting(4)
+ private boolean workerLinked;
+ @Persisting(value = 5, nullable = true)
+ private String hqToken;
+ @Persisting(6)
+ private Date hqTokenExpiration;
+
+ public ConnectLinkedAppRecordV3() {
+ hqTokenExpiration = new Date();
+ }
+
+ public ConnectLinkedAppRecordV3(String appId, String userId, String password) {
+ this.appId = appId;
+ this.userId = userId;
+ this.password = password;
+
+ hqTokenExpiration = new Date();
+ }
+
+ public String getAppId() { return appId; }
+ public String getUserId() {
+ return userId;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public boolean getWorkerLinked() {
+ return workerLinked;
+ }
+
+ public void setWorkerLinked(boolean linked) {
+ workerLinked = linked;
+ }
+
+ public String getHqToken() {
+ return hqToken;
+ }
+
+ public Date getHqTokenExpiration() {
+ return hqTokenExpiration;
+ }
+
+ public void updateHqToken(String token, Date expirationDate) {
+ hqToken = token;
+ hqTokenExpiration = expirationDate;
+ }
+}
diff --git a/app/src/org/commcare/fragments/connect/ConnectDeliveryProgressFragment.java b/app/src/org/commcare/fragments/connect/ConnectDeliveryProgressFragment.java
index 0519cddbfb..9c626221fd 100644
--- a/app/src/org/commcare/fragments/connect/ConnectDeliveryProgressFragment.java
+++ b/app/src/org/commcare/fragments/connect/ConnectDeliveryProgressFragment.java
@@ -1,5 +1,6 @@
package org.commcare.fragments.connect;
+import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
@@ -34,6 +35,7 @@
import java.util.Locale;
import androidx.annotation.NonNull;
+import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
@@ -50,6 +52,10 @@ public class ConnectDeliveryProgressFragment extends Fragment {
private ConnectDeliveryProgressFragment.ViewStateAdapter viewStateAdapter;
private TextView updateText;
+ private ConstraintLayout paymentAlertTile;
+ private TextView paymentAlertText;
+ private ConnectJobPaymentRecord paymentToConfirm = null;
+
public ConnectDeliveryProgressFragment() {
// Required empty public constructor
}
@@ -75,8 +81,29 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container,
updateUpdatedDate(job.getLastDeliveryUpdate());
ImageView refreshButton = view.findViewById(R.id.connect_delivery_refresh);
- refreshButton.setOnClickListener(v -> {
- refreshData();
+ refreshButton.setOnClickListener(v -> refreshData());
+
+ paymentAlertTile = view.findViewById(R.id.connect_delivery_progress_alert_tile);
+ paymentAlertText = view.findViewById(R.id.connect_payment_confirm_label);
+ TextView paymentAlertNoButton = view.findViewById(R.id.connect_payment_confirm_no_button);
+ paymentAlertNoButton.setOnClickListener(v -> {
+ updatePaymentConfirmationTile(getContext(), true);
+ FirebaseAnalyticsUtil.reportCccPaymentConfirmationInteraction(false);
+ });
+
+ TextView paymentAlertYesButton = view.findViewById(R.id.connect_payment_confirm_yes_button);
+ paymentAlertYesButton.setOnClickListener(v -> {
+ final ConnectJobPaymentRecord payment = paymentToConfirm;
+ //Dismiss the tile
+ updatePaymentConfirmationTile(getContext(), true);
+
+ if(payment != null) {
+ FirebaseAnalyticsUtil.reportCccPaymentConfirmationInteraction(true);
+
+ ConnectManager.updatePaymentConfirmed(getContext(), payment, true, success -> {
+ FirebaseAnalyticsUtil.reportCccApiPaymentConfirmation(success);
+ });
+ }
});
final ViewPager2 pager = view.findViewById(R.id.connect_delivery_progress_view_pager);
@@ -114,6 +141,8 @@ public void onTabReselected(TabLayout.Tab tab) {
}
});
+ updatePaymentConfirmationTile(getContext(), false);
+
return view;
}
@@ -193,6 +222,7 @@ public void processSuccess(int responseCode, InputStream responseData) {
try {
updateUpdatedDate(new Date());
+ updatePaymentConfirmationTile(getContext(), false);
viewStateAdapter.refresh();
}
catch(Exception e) {
@@ -221,6 +251,35 @@ public void processNetworkFailure() {
});
}
+ private void updatePaymentConfirmationTile(Context context, boolean forceHide) {
+ paymentToConfirm = null;
+ if(!forceHide) {
+ //Look for at least one payment that needs to be confirmed
+ for (ConnectJobPaymentRecord payment : job.getPayments()) {
+ if(payment.allowConfirm()) {
+ paymentToConfirm = payment;
+ break;
+ }
+ }
+ }
+
+ //NOTE: Checking for network connectivity here
+ boolean show = paymentToConfirm != null;
+ if(show) {
+ show = ConnectNetworkHelper.isOnline(context);
+ FirebaseAnalyticsUtil.reportCccPaymentConfirmationOnlineCheck(show);
+ }
+
+ paymentAlertTile.setVisibility(show ? View.VISIBLE : View.GONE);
+ if(show) {
+ DateFormat df = new SimpleDateFormat("MM/dd/yyyy", Locale.getDefault());
+ String date = df.format(paymentToConfirm.getDate());
+ paymentAlertText.setText(getString(R.string.connect_payment_confirm_text, paymentToConfirm.getAmount(), job.getCurrency(), date));
+
+ FirebaseAnalyticsUtil.reportCccPaymentConfirmationDisplayed();
+ }
+ }
+
private void reportApiCall(boolean success) {
FirebaseAnalyticsUtil.reportCccApiDeliveryProgress(success);
}
diff --git a/app/src/org/commcare/fragments/connect/ConnectJobIntroFragment.java b/app/src/org/commcare/fragments/connect/ConnectJobIntroFragment.java
index 391a6fb3d6..40d35e3aca 100644
--- a/app/src/org/commcare/fragments/connect/ConnectJobIntroFragment.java
+++ b/app/src/org/commcare/fragments/connect/ConnectJobIntroFragment.java
@@ -106,7 +106,7 @@ public void processSuccess(int responseCode, InputStream responseData) {
public void processFailure(int responseCode, IOException e) {
Toast.makeText(getContext(), "Connect: error starting learning", Toast.LENGTH_SHORT).show();
reportApiCall(false);
- //TODO DAV: Log the message from the server
+ //TODO: Log the message from the server
}
@Override
diff --git a/app/src/org/commcare/fragments/connect/ConnectResultsListFragment.java b/app/src/org/commcare/fragments/connect/ConnectResultsListFragment.java
index 1aa77e5d01..6164342a0a 100644
--- a/app/src/org/commcare/fragments/connect/ConnectResultsListFragment.java
+++ b/app/src/org/commcare/fragments/connect/ConnectResultsListFragment.java
@@ -7,6 +7,7 @@
import android.view.ViewGroup;
import android.widget.TextView;
+import org.commcare.activities.connect.ConnectManager;
import org.commcare.android.database.connect.models.ConnectJobDeliveryRecord;
import org.commcare.android.database.connect.models.ConnectJobPaymentRecord;
import org.commcare.android.database.connect.models.ConnectJobRecord;
@@ -100,13 +101,22 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi
verificationHolder.statusText.setText(delivery.getStatus());
verificationHolder.reasonText.setText(delivery.getReason());
} else if(holder instanceof PaymentViewHolder paymentHolder) {
- ConnectJobPaymentRecord payment = job.getPayments().get(position);
+ final ConnectJobPaymentRecord payment = job.getPayments().get(position);
String money = job.getMoneyString(Integer.parseInt(payment.getAmount()));
+ paymentHolder.nameText.setText(parentContext.getString(R.string.connect_results_payment_description, money));
- paymentHolder.nameText.setText(parentContext.getString(R.string.connect_results_payment_title));
- paymentHolder.dateText.setText(df.format(payment.getDate()));
- paymentHolder.statusText.setText(parentContext.getString(R.string.connect_results_payment_description, money));
+ paymentHolder.dateText.setText(parentContext.getString(R.string.connect_results_payment_date, df.format(payment.getDate())));
+
+ boolean enabled = paymentHolder.updateConfirmedText(parentContext, payment);
+
+ if(enabled) {
+ paymentHolder.confirmText.setOnClickListener(v -> {
+ ConnectManager.updatePaymentConfirmed(parentContext, payment, !payment.getConfirmed(), success -> {
+ paymentHolder.updateConfirmedText(parentContext, payment);
+ });
+ });
+ }
}
}
@@ -134,14 +144,35 @@ public VerificationViewHolder(@NonNull View itemView) {
public static class PaymentViewHolder extends RecyclerView.ViewHolder {
TextView nameText;
TextView dateText;
- TextView statusText;
+ TextView confirmText;
public PaymentViewHolder(@NonNull View itemView) {
super(itemView);
nameText = itemView.findViewById(R.id.name);
dateText = itemView.findViewById(R.id.date);
- statusText = itemView.findViewById(R.id.status);
+ confirmText = itemView.findViewById(R.id.confirm);
+ }
+
+ public boolean updateConfirmedText(Context context, ConnectJobPaymentRecord payment) {
+ boolean enabled;
+ int confirmTextId;
+ if(payment.getConfirmed()) {
+ enabled = payment.allowConfirmUndo();
+ confirmTextId = enabled ?
+ R.string.connect_results_payment_confirm_undo :
+ R.string.connect_results_payment_confirmed;
+ } else {
+ enabled = payment.allowConfirm();
+ confirmTextId = enabled ?
+ R.string.connect_results_payment_confirm :
+ R.string.connect_results_payment_not_confirmed;
+ }
+
+ confirmText.setText(confirmTextId);
+ confirmText.setTextColor(context.getResources().getColor(enabled ? R.color.blue : R.color.dark_grey));
+
+ return enabled;
}
}
}
diff --git a/app/src/org/commcare/google/services/analytics/CCAnalyticsEvent.java b/app/src/org/commcare/google/services/analytics/CCAnalyticsEvent.java
index 9230fc8d7f..ab80137ac9 100644
--- a/app/src/org/commcare/google/services/analytics/CCAnalyticsEvent.java
+++ b/app/src/org/commcare/google/services/analytics/CCAnalyticsEvent.java
@@ -47,5 +47,9 @@ public class CCAnalyticsEvent {
static final String CCC_API_LEARN_PROGRESS = "ccc_api_learn_progress";
static final String CCC_API_CLAIM_JOB = "ccc_api_claim_job";
static final String CCC_API_DELIVERY_PROGRESS = "ccc_api_delivery_progress";
+ static final String CCC_API_PAYMENT_CONFIRMATION = "ccc_api_payment_confirmation";
+ static final String CCC_PAYMENT_CONFIRMATION_CHECK = "ccc_payment_confirmation_check";
+ static final String CCC_PAYMENT_CONFIRMATION_DISPLAY = "ccc_payment_confirmation_display";
+ static final String CCC_PAYMENT_CONFIRMATION_INTERACT = "ccc_payment_confirmation_interact";
}
diff --git a/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java b/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java
index 9edda78f11..91021434f1 100644
--- a/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java
+++ b/app/src/org/commcare/google/services/analytics/FirebaseAnalyticsUtil.java
@@ -409,6 +409,29 @@ public static void reportCccApiDeliveryProgress(boolean success) {
reportEvent(CCAnalyticsEvent.CCC_API_DELIVERY_PROGRESS, b);
}
+ public static void reportCccApiPaymentConfirmation(boolean success) {
+ Bundle b = new Bundle();
+ b.putLong(CCAnalyticsEvent.PARAM_API_SUCCESS, success ? 1 : 0);
+ reportEvent(CCAnalyticsEvent.CCC_API_PAYMENT_CONFIRMATION, b);
+ }
+
+ public static void reportCccPaymentConfirmationOnlineCheck(boolean success) {
+ Bundle b = new Bundle();
+ b.putLong(CCAnalyticsEvent.PARAM_API_SUCCESS, success ? 1 : 0);
+ reportEvent(CCAnalyticsEvent.CCC_PAYMENT_CONFIRMATION_CHECK, b);
+ }
+
+ public static void reportCccPaymentConfirmationDisplayed() {
+ Bundle b = new Bundle();
+ reportEvent(CCAnalyticsEvent.CCC_PAYMENT_CONFIRMATION_DISPLAY, b);
+ }
+
+ public static void reportCccPaymentConfirmationInteraction(boolean positive) {
+ Bundle b = new Bundle();
+ b.putLong(CCAnalyticsEvent.PARAM_API_SUCCESS, positive ? 1 : 0);
+ reportEvent(CCAnalyticsEvent.CCC_PAYMENT_CONFIRMATION_INTERACT, b);
+ }
+
public static void reportCccSignOut() {
reportEvent(CCAnalyticsEvent.CCC_SIGN_OUT);
}
diff --git a/app/src/org/commcare/models/database/connect/ConnectDatabaseUpgrader.java b/app/src/org/commcare/models/database/connect/ConnectDatabaseUpgrader.java
index bb575bbc33..f5427f95c3 100644
--- a/app/src/org/commcare/models/database/connect/ConnectDatabaseUpgrader.java
+++ b/app/src/org/commcare/models/database/connect/ConnectDatabaseUpgrader.java
@@ -12,16 +12,22 @@
import org.commcare.android.database.connect.models.ConnectJobDeliveryRecordV2;
import org.commcare.android.database.connect.models.ConnectJobLearningRecord;
import org.commcare.android.database.connect.models.ConnectJobPaymentRecord;
+import org.commcare.android.database.connect.models.ConnectJobPaymentRecordV3;
import org.commcare.android.database.connect.models.ConnectJobRecord;
import org.commcare.android.database.connect.models.ConnectJobRecordV2;
import org.commcare.android.database.connect.models.ConnectLearnModuleSummaryRecord;
+import org.commcare.android.database.connect.models.ConnectLinkedAppRecord;
+import org.commcare.android.database.connect.models.ConnectLinkedAppRecordV3;
import org.commcare.models.database.ConcreteAndroidDbHelper;
import org.commcare.models.database.DbUtil;
import org.commcare.models.database.SqlStorage;
+import org.commcare.models.framework.Persisting;
import org.commcare.modern.database.TableBuilder;
import org.commcare.resources.model.Resource;
import org.javarosa.core.services.storage.Persistable;
+import java.util.Date;
+
public class ConnectDatabaseUpgrader {
private final Context c;
@@ -41,6 +47,12 @@ public void upgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
oldVersion = 3;
}
}
+
+ if (oldVersion == 3) {
+ if (upgradeThreeFour(db)) {
+ oldVersion = 4;
+ }
+ }
}
private boolean upgradeOneTwo(SQLiteDatabase db) {
@@ -113,6 +125,97 @@ private boolean upgradeTwoThree(SQLiteDatabase db) {
}
}
+ private boolean upgradeThreeFour(SQLiteDatabase db) {
+ db.beginTransaction();
+
+ try {
+ //First, migrate the old ConnectLinkedAppRecord in storage to the new version
+ db.execSQL(DbUtil.addColumnToTable(
+ ConnectLinkedAppRecord.STORAGE_KEY,
+ ConnectLinkedAppRecord.META_CONNECTID_LINKED,
+ "TEXT"));
+
+ db.execSQL(DbUtil.addColumnToTable(
+ ConnectLinkedAppRecord.STORAGE_KEY,
+ ConnectLinkedAppRecord.META_OFFERED_1,
+ "TEXT"));
+
+ db.execSQL(DbUtil.addColumnToTable(
+ ConnectLinkedAppRecord.STORAGE_KEY,
+ ConnectLinkedAppRecord.META_OFFERED_1_DATE,
+ "TEXT"));
+
+ db.execSQL(DbUtil.addColumnToTable(
+ ConnectLinkedAppRecord.STORAGE_KEY,
+ ConnectLinkedAppRecord.META_OFFERED_2,
+ "TEXT"));
+
+ db.execSQL(DbUtil.addColumnToTable(
+ ConnectLinkedAppRecord.STORAGE_KEY,
+ ConnectLinkedAppRecord.META_OFFERED_2_DATE,
+ "TEXT"));
+
+ SqlStorage oldStorage = new SqlStorage<>(
+ ConnectLinkedAppRecord.STORAGE_KEY,
+ ConnectLinkedAppRecordV3.class,
+ new ConcreteAndroidDbHelper(c, db));
+
+ SqlStorage newStorage = new SqlStorage<>(
+ ConnectLinkedAppRecord.STORAGE_KEY,
+ ConnectLinkedAppRecord.class,
+ new ConcreteAndroidDbHelper(c, db));
+
+ for (Persistable r : oldStorage) {
+ ConnectLinkedAppRecordV3 oldRecord = (ConnectLinkedAppRecordV3)r;
+ ConnectLinkedAppRecord newRecord = ConnectLinkedAppRecord.fromV3(oldRecord);
+ //set this new record to have same ID as the old one
+ newRecord.setID(oldRecord.getID());
+ newStorage.write(newRecord);
+ }
+
+ //Next, migrate the old ConnectJobPaymentRecord in storage to the new version
+ db.execSQL(DbUtil.addColumnToTable(
+ ConnectJobPaymentRecord.STORAGE_KEY,
+ ConnectJobPaymentRecord.META_PAYMENT_ID,
+ "TEXT"));
+
+ db.execSQL(DbUtil.addColumnToTable(
+ ConnectJobPaymentRecord.STORAGE_KEY,
+ ConnectJobPaymentRecord.META_CONFIRMED,
+ "TEXT"));
+
+ db.execSQL(DbUtil.addColumnToTable(
+ ConnectJobPaymentRecord.STORAGE_KEY,
+ ConnectJobPaymentRecord.META_CONFIRMED_DATE,
+ "TEXT"));
+
+ oldStorage = new SqlStorage<>(
+ ConnectJobPaymentRecord.STORAGE_KEY,
+ ConnectJobPaymentRecordV3.class,
+ new ConcreteAndroidDbHelper(c, db));
+
+ newStorage = new SqlStorage<>(
+ ConnectJobPaymentRecord.STORAGE_KEY,
+ ConnectJobPaymentRecord.class,
+ new ConcreteAndroidDbHelper(c, db));
+
+ for (Persistable r : oldStorage) {
+ ConnectJobPaymentRecordV3 oldRecord = (ConnectJobPaymentRecordV3)r;
+ ConnectJobPaymentRecord newRecord = ConnectJobPaymentRecord.fromV3(oldRecord);
+ //set this new record to have same ID as the old one
+ newRecord.setID(oldRecord.getID());
+ newStorage.write(newRecord);
+ }
+
+ db.setTransactionSuccessful();
+ return true;
+ } catch(Exception e) {
+ return false;
+ } finally {
+ db.endTransaction();
+ }
+ }
+
private static boolean addTableForNewModel(SQLiteDatabase db, String storageKey,
Persistable modelToAdd) {
db.beginTransaction();
diff --git a/app/src/org/commcare/models/database/connect/DatabaseConnectOpenHelper.java b/app/src/org/commcare/models/database/connect/DatabaseConnectOpenHelper.java
index 112e526054..9b50070e7c 100644
--- a/app/src/org/commcare/models/database/connect/DatabaseConnectOpenHelper.java
+++ b/app/src/org/commcare/models/database/connect/DatabaseConnectOpenHelper.java
@@ -30,8 +30,10 @@ public class DatabaseConnectOpenHelper extends SQLiteOpenHelper {
* V.2 - Added ConnectJobRecord, ConnectAppInfo, and ConnectLearningModuleInfo tables
* V.3 - Added date_claimed column to ConnectJobRecord,
* and reason column to ConnectJobDeliveryRecord
+ * V.4 - Added confirmed and confirmedDate fields to ConnectJobPaymentRecord
+ * Added link offer info to ConnectLinkedAppRecord
*/
- private static final int CONNECT_DB_VERSION = 3;
+ private static final int CONNECT_DB_VERSION = 4;
private static final String CONNECT_DB_LOCATOR = "database_connect";
diff --git a/app/src/org/commcare/network/CommcareRequestGenerator.java b/app/src/org/commcare/network/CommcareRequestGenerator.java
index bdb6bbea6c..c15ae23e59 100755
--- a/app/src/org/commcare/network/CommcareRequestGenerator.java
+++ b/app/src/org/commcare/network/CommcareRequestGenerator.java
@@ -170,7 +170,7 @@ private AuthInfo buildAuth() {
AuthInfo authInfo = new AuthInfo.NoAuth();
if (username != null) {
try {
- AuthInfo.TokenAuth tokenAuth = ConnectSsoHelper.acquireSsoTokenSync(CommCareApplication.instance(), username);
+ AuthInfo.TokenAuth tokenAuth = ConnectSsoHelper.retrieveHqSsoTokenSync(CommCareApplication.instance(), username, false);
if (tokenAuth != null) {
authInfo = tokenAuth;
} else {
diff --git a/commcare-support-library/src/main/java/org/commcare/commcaresupportlibrary/CommCareLauncher.java b/commcare-support-library/src/main/java/org/commcare/commcaresupportlibrary/CommCareLauncher.java
index c137e8b7f2..be008902be 100644
--- a/commcare-support-library/src/main/java/org/commcare/commcaresupportlibrary/CommCareLauncher.java
+++ b/commcare-support-library/src/main/java/org/commcare/commcaresupportlibrary/CommCareLauncher.java
@@ -8,31 +8,11 @@
*/
public class CommCareLauncher {
public static final String SESSION_ENDPOINT_APP_ID = "ccodk_session_endpoint_app_id";
- public static final String EXTRA_FROM_CONNECT = "extra_from_connect";
private static final String CC_LAUNCH_ACTION = "org.commcare.dalvik.action.CommCareSession";
- /**
- *
- * @param context Android context to launch the CommCare with
- * @param appId Unique Id for CommCare App that CommCare should launch with
- */
public static void launchCommCareForAppId(Context context, String appId) {
- launchCommCareForAppId(context, appId, false);
- }
-
- /**
- *
- * @param context Android context to launch the CommCare with
- * @param appId Unique Id for CommCare App that CommCare should launch with
- */
- public static void launchCommCareForAppIdFromConnect(Context context, String appId) {
- launchCommCareForAppId(context, appId, true);
- }
-
- private static void launchCommCareForAppId(Context context, String appId, boolean fromConnect) {
Intent intent = new Intent(CC_LAUNCH_ACTION);
intent.putExtra(SESSION_ENDPOINT_APP_ID, appId);
- intent.putExtra(EXTRA_FROM_CONNECT, fromConnect);
context.startActivity(intent);
}
}