Skip to content

Commit

Permalink
Merge pull request #192 from shankari/final_android_fixes
Browse files Browse the repository at this point in the history
Handle android 11 "always" permission properly + Humanize android notification messages
  • Loading branch information
shankari authored Jun 5, 2021
2 parents dc48073 + a726584 commit 8c3e473
Show file tree
Hide file tree
Showing 7 changed files with 87 additions and 16 deletions.
9 changes: 7 additions & 2 deletions res/android/values/dc_strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,14 @@
<string name="unknown_error_location_settings">Unknown error while reading location, please check your settings</string>
<string name="notify_curr_state">In state %1$s</string>
<!-- TripDiaryStateMachineForegroundService -->
<string name="foreground_killed_email_log">Foreground service killed, Profile -> Email Log for debugging</string>
<string name="foreground_killed_email_log">Foreground service killed, report at Profile -> Upload Log</string>
<!-- ConfigManager -->
<string name="error_reading_stored_config">Error reading stored tracking config, reset to defaults</string>
<!-- DataCollectionPlugin -->
<string name="unable_resolve_issue">Unrecoverable error in tracking. Profile -> Email Log for further investigation</string>
<string name="unable_resolve_issue">Unrecoverable error in tracking, report at Profile -> Upload Log</string>
<!-- State notification displays-->
<string name="local.state.waiting_for_trip_start">Ready for your next trip</string>
<string name="local.state.ongoing_trip">Yay! You are on a trip, keep going!</string>
<string name="local.state.start">Cannot start app, see next pop up</string>
<string name="local.state.tracking_stopped">Stopped tracking; remember to re-enable</string>
</resources>
5 changes: 5 additions & 0 deletions src/android/DataCollectionPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
/*
Expand Down
14 changes: 7 additions & 7 deletions src/android/location/ForegroundServiceComm.java
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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);
}
}

Expand Down
26 changes: 23 additions & 3 deletions src/android/location/TripDiaryStateMachineForegroundService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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));
}
Expand Down
2 changes: 1 addition & 1 deletion src/android/location/TripDiaryStateMachineService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

Expand Down
45 changes: 43 additions & 2 deletions src/android/verification/SensorControlForegroundDelegate.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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)) {
Expand Down Expand Up @@ -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);
Expand All @@ -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...");
}
Expand Down

0 comments on commit 8c3e473

Please sign in to comment.