diff --git a/res/android/values/dc_strings.xml b/res/android/values/dc_strings.xml
index 4b76e9db..ffb0085d 100644
--- a/res/android/values/dc_strings.xml
+++ b/res/android/values/dc_strings.xml
@@ -24,9 +24,14 @@
Unknown error while reading location, please check your settings
In state %1$s
- Foreground service killed, Profile -> Email Log for debugging
+ Foreground service killed, report at Profile -> Upload Log
Error reading stored tracking config, reset to defaults
- Unrecoverable error in tracking. Profile -> Email Log for further investigation
+ Unrecoverable error in tracking, report at Profile -> Upload Log
+
+ Ready for your next trip
+ Yay! You are on a trip, keep going!
+ Cannot start app, see next pop up
+ Stopped tracking; remember to re-enable
diff --git a/src/android/DataCollectionPlugin.java b/src/android/DataCollectionPlugin.java
index 53304015..d399f918 100644
--- a/src/android/DataCollectionPlugin.java
+++ b/src/android/DataCollectionPlugin.java
@@ -161,8 +161,13 @@ public void onRequestPermissionResult(int requestCode, String[] permissions,
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (data == null) {
+ Log.d(cordova.getActivity(), TAG, "received onActivityResult(" + requestCode + "," +
+ resultCode + "," + "null data" + ")");
+ } else {
Log.d(cordova.getActivity(), TAG, "received onActivityResult("+requestCode+","+
resultCode+","+data.getDataString()+")");
+ }
// This will be a NOP if we are not handling the correct activity intent
mControlDelegate.onActivityResult(requestCode, resultCode, data);
/*
diff --git a/src/android/location/ForegroundServiceComm.java b/src/android/location/ForegroundServiceComm.java
index 3a15c681..bce42e37 100644
--- a/src/android/location/ForegroundServiceComm.java
+++ b/src/android/location/ForegroundServiceComm.java
@@ -20,27 +20,27 @@ public class ForegroundServiceComm {
private TripDiaryStateMachineForegroundService mService;
private boolean mBound = false;
private int nRecursion = 0;
- private String pendingMsg = null;
+ private String pendingMsgState = null;
public ForegroundServiceComm(Context context) {
mCtxt = context;
mCtxt.bindService(getForegroundServiceIntent(), connection, 0);
}
- public void setMessage(String msg) {
+ public void setNewState(String newState) {
nRecursion++;
if (mBound) {
Log.d(mCtxt, TAG, "Service successfully bound, setting state");
- mService.setStateMessage(msg);
+ mService.setStateMessage(newState);
} else {
Intent fsi = getForegroundServiceIntent();
if (nRecursion < 5) {
Log.d(mCtxt, TAG, "nRecursion = "+nRecursion+" rebinding ");
- pendingMsg = msg;
+ pendingMsgState = newState;
mCtxt.bindService(fsi, connection, 0);
} else if (nRecursion < 10) {
Log.d(mCtxt, TAG, "nRecursion = "+nRecursion+" restarting before rebind ");
- pendingMsg = msg;
+ pendingMsgState = newState;
TripDiaryStateMachineForegroundService.startProperly(mCtxt);
mCtxt.bindService(fsi, connection, 0);
} else {
@@ -67,8 +67,8 @@ public void onServiceConnected(ComponentName className,
mService = binder.getService();
Log.e(mCtxt, TAG, "Successfully bound to service "+ mService);
mBound = true;
- if (pendingMsg != null) {
- setMessage(pendingMsg);
+ if (pendingMsgState != null) {
+ setNewState(pendingMsgState);
}
}
diff --git a/src/android/location/TripDiaryStateMachineForegroundService.java b/src/android/location/TripDiaryStateMachineForegroundService.java
index c618ed53..8ce73e9e 100644
--- a/src/android/location/TripDiaryStateMachineForegroundService.java
+++ b/src/android/location/TripDiaryStateMachineForegroundService.java
@@ -46,14 +46,33 @@ public void onCreate() {
Log.d(this, TAG, "onCreate called");
}
+ /*
+ * Convert the current state, which is a string (e.g.
+ * waiting_for_trip_start) into a more human-friendly string (e.g. Ready to
+ * go). Since the string mappings expect an id, we need to look up the ID
+ * first using getResources and then the string.
+ * https://stackoverflow.com/a/19093447/4040267
+ * Fallback to the old "In state XXX" if there is no mapping because
+ * otherwise the app will crash.
+ */
+ public static String humanizeState(Context ctxt, String state) {
+ try {
+ int resId = ctxt.getResources().getIdentifier(state, "string", ctxt.getPackageName());
+ return ctxt.getString(resId);
+ } catch (android.content.res.Resources.NotFoundException e) {
+ Log.e(ctxt, TAG, "ResourcesNotFoundException while humanizing message, falling back to auto-generated");
+ return ctxt.getString(R.string.notify_curr_state, state);
+ }
+ }
+
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(this, TAG, "onStartCommand called with intent = "+intent+
" flags = " + flags + " and startId = " + startId);
- String message = this.getString(R.string.notify_curr_state, TripDiaryStateMachineService.getState(this));
+ String message = humanizeState(this, TripDiaryStateMachineService.getState(this));
if (intent == null) {
SensorControlBackgroundChecker.checkLocationSettingsAndPermissions(this);
- message = this.getString(R.string.notify_curr_state, TripDiaryStateMachineService.getState(this));
+ message = humanizeState(this, TripDiaryStateMachineService.getState(this));
}
handleStart(message, intent, flags, startId);
// We want this service to continue running until it is explicitly
@@ -103,7 +122,8 @@ public IBinder onBind(Intent intent) {
return mBinder;
}
- public void setStateMessage(String message) {
+ public void setStateMessage(String newState) {
+ String message = humanizeState(this, newState);
NotificationManager nMgr = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
nMgr.notify(ONGOING_TRIP_ID, getNotification(message));
}
diff --git a/src/android/location/TripDiaryStateMachineService.java b/src/android/location/TripDiaryStateMachineService.java
index 5838b0fc..b65ee627 100644
--- a/src/android/location/TripDiaryStateMachineService.java
+++ b/src/android/location/TripDiaryStateMachineService.java
@@ -110,7 +110,7 @@ public void setNewState(String newState, boolean doChecks) {
Log.d(this, TAG, "newState saved in prefManager is "+
PreferenceManager.getDefaultSharedPreferences(this).getString(
this.getString(R.string.curr_state_key), "not found"));
- mComm.setMessage(this.getString(R.string.notify_curr_state, newState));
+ mComm.setNewState(newState);
// Let's check the location settings every time we change the state instead of only on failure
// This makes the rest of the code much simpler, allows us to catch issues as quickly as possible,
// and
diff --git a/src/android/location/TripDiaryStateMachineServiceOngoing.java b/src/android/location/TripDiaryStateMachineServiceOngoing.java
index 22d2137e..af2019ad 100644
--- a/src/android/location/TripDiaryStateMachineServiceOngoing.java
+++ b/src/android/location/TripDiaryStateMachineServiceOngoing.java
@@ -114,7 +114,7 @@ public void setNewState(String newState) {
Log.d(this, TAG, "newState saved in prefManager is "+
PreferenceManager.getDefaultSharedPreferences(this).getString(
this.getString(R.string.curr_state_key), "not found"));
- mComm.setMessage(this.getString(R.string.notify_curr_state, newState));
+ mComm.setNewState(newState);
stopSelf();
}
diff --git a/src/android/verification/SensorControlForegroundDelegate.java b/src/android/verification/SensorControlForegroundDelegate.java
index b66dffc2..ff317238 100644
--- a/src/android/verification/SensorControlForegroundDelegate.java
+++ b/src/android/verification/SensorControlForegroundDelegate.java
@@ -23,7 +23,10 @@
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
+import android.net.Uri;
import android.os.Build;
+import android.provider.Settings;
+
import com.google.android.gms.location.LocationSettingsStates;
@@ -48,10 +51,34 @@ public void checkAndPromptPermissions() {
}
private void checkAndPromptLocationPermissions() {
- if(cordova.hasPermission(SensorControlConstants.LOCATION_PERMISSION) && cordova.hasPermission(SensorControlConstants.BACKGROUND_LOC_PERMISSION)) {
+ if(cordova.hasPermission(SensorControlConstants.LOCATION_PERMISSION) &&
+ cordova.hasPermission(SensorControlConstants.BACKGROUND_LOC_PERMISSION)) {
SensorControlBackgroundChecker.restartFSMIfStartState(cordova.getActivity());
return;
}
+ // If this is android 11 (API 30), we want to launch the app settings instead of prompting for permission
+ // because the default permission prompting does not offer "always" as an option
+ // https://github.com/e-mission/e-mission-docs/issues/608
+ // we don't really care about which level of permission is missing since the prompt doesn't
+ // do anything anyway. If either permission is missing, we just open the app settings
+ // Note also that we should actually check for VERSION_CODES.R
+ // but since we are not targeting API 30 yet, we can't do that
+ // so we use Q (29) + 1 instead. I think that is more readable than 30
+ if ((Build.VERSION.SDK_INT >= (Build.VERSION_CODES.Q + 1)) &&
+ (!cordova.hasPermission(SensorControlConstants.LOCATION_PERMISSION) ||
+ !cordova.hasPermission(SensorControlConstants.BACKGROUND_LOC_PERMISSION))) {
+ Activity mAct = cordova.getActivity();
+ String msgString = " LOC = "+cordova.hasPermission(SensorControlConstants.LOCATION_PERMISSION)+
+ " BACKGROUND LOC "+ cordova.hasPermission(SensorControlConstants.BACKGROUND_LOC_PERMISSION)+
+ " Android R+, so opening app settings anyway";
+ Log.i(cordova.getActivity(), TAG, msgString);
+ // These are to hopefully help us get a callback once the settings are changed
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
+ intent.setData(Uri.fromParts("package", mAct.getPackageName(), null));
+ cordova.setActivityResultCallback(plugin);
+ mAct.startActivityForResult(intent, SensorControlConstants.ENABLE_BOTH_PERMISSION);
+ return;
+ }
if(!cordova.hasPermission(SensorControlConstants.LOCATION_PERMISSION) &&
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) &&
!cordova.hasPermission(SensorControlConstants.BACKGROUND_LOC_PERMISSION)) {
@@ -174,9 +201,9 @@ public void onRequestPermissionResult(int requestCode, String[] permissions,
public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Activity mAct = cordova.getActivity();
switch (requestCode) {
case SensorControlConstants.ENABLE_LOCATION_SETTINGS:
- Activity mAct = cordova.getActivity();
Log.d(mAct, TAG, requestCode + " is our code, handling callback");
cordova.setActivityResultCallback(null);
final LocationSettingsStates states = LocationSettingsStates.fromIntent(data);
@@ -196,6 +223,20 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.e(cordova.getActivity(), TAG, "Unknown result code while enabling location " + resultCode);
break;
}
+ case SensorControlConstants.ENABLE_BOTH_PERMISSION:
+ Log.d(mAct, TAG, requestCode + " is our code, handling callback");
+ cordova.setActivityResultCallback(null);
+ Log.d(mAct, TAG, "Got permission callback from launching app settings");
+ if (cordova.hasPermission(SensorControlConstants.LOCATION_PERMISSION)) {
+ // location permission enabled, cancelling notification
+ NotificationHelper.cancelNotification(cordova.getActivity(), SensorControlConstants.ENABLE_LOCATION_PERMISSION);
+ }
+ if (cordova.hasPermission(SensorControlConstants.BACKGROUND_LOC_PERMISSION)) {
+ // background location permission enabled, cancelling notification
+ NotificationHelper.cancelNotification(cordova.getActivity(), SensorControlConstants.ENABLE_BACKGROUND_LOC_PERMISSION);
+ }
+ SensorControlBackgroundChecker.restartFSMIfStartState(cordova.getActivity());
+ break;
default:
Log.d(cordova.getActivity(), TAG, "Got unsupported request code " + requestCode + " , ignoring...");
}