Skip to content

Commit

Permalink
Merge pull request #170 from shankari/implement_runtime_permissions
Browse files Browse the repository at this point in the history
Implement runtime permissions
  • Loading branch information
shankari authored Mar 13, 2019
2 parents 2dce243 + 9641339 commit 166ae87
Show file tree
Hide file tree
Showing 13 changed files with 364 additions and 135 deletions.
7 changes: 4 additions & 3 deletions plugin.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
id="edu.berkeley.eecs.emission.cordova.datacollection"
version="1.2.0">
version="1.3.0">

<name>DataCollection</name>
<description>Background data collection FTW! This is the part that I really
Expand Down Expand Up @@ -73,7 +73,6 @@
<action android:name="local.transition.stop_tracking"></action>
<action android:name="local.transition.start_tracking"></action>
<action android:name="local.transition.tracking_error"></action>
<action android:name="android.location.MODE_CHANGED"></action>
</intent-filter>
</receiver>
<service
Expand Down Expand Up @@ -114,11 +113,13 @@
</config-file>

<framework src="com.google.code.gson:gson:+"/>
<framework src="com.google.android.gms:play-services-location:10.2+"/>
<framework src="com.google.android.gms:play-services-location:$LOCATION_VERSION"/>
<preference name="LOCATION_VERSION" default="11.0.1"/>

<source-file src="src/android/DataCollectionPlugin.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker"/>
<source-file src="src/android/BootReceiver.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker"/>
<source-file src="src/android/Constants.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker"/>
<source-file src="src/android/ExplicitIntent.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker"/>
<source-file src="src/android/GooglePlayChecker.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker"/>
<source-file src="src/android/ConfigManager.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker"/>
<source-file src="src/android/location/ActivityRecognitionChangeIntentService.java" target-dir="src/edu/berkeley/eecs/emission/cordova/tracker/location"/>
Expand Down
2 changes: 1 addition & 1 deletion src/android/BootReceiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void onReceive(Context ctx, Intent intent) {
// Re-initialize the state machine if we haven't stopped tracking
if (!TripDiaryStateMachineService.getState(ctx).equals(
ctx.getString(R.string.state_tracking_stopped))) {
ctx.sendBroadcast(new Intent(ctx.getString(R.string.transition_initialize)));
ctx.sendBroadcast(new ExplicitIntent(ctx, R.string.transition_initialize));
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/android/ConfigManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,12 @@
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;

import org.apache.cordova.ConfigXmlParser;

import java.util.HashMap;

import edu.berkeley.eecs.emission.R;

import edu.berkeley.eecs.emission.cordova.tracker.wrapper.ConsentConfig;
import edu.berkeley.eecs.emission.cordova.tracker.wrapper.LocationTrackingConfig;
import edu.berkeley.eecs.emission.cordova.unifiedlogger.Log;
Expand Down Expand Up @@ -53,6 +56,13 @@ protected static void updateConfig(Context context, LocationTrackingConfig newCo
cachedConfig = newConfig;
}

public static String getReqConsent(Context ctxt) {
ConfigXmlParser parser = new ConfigXmlParser();
parser.parse(ctxt);
String reqConsent = parser.getPreferences().getString("emSensorDataCollectionProtocolApprovalDate", null);
return reqConsent;
}

public static boolean isConsented(Context context, String reqConsent) {
try {
ConsentConfig currConfig = UserCacheFactory.getUserCache(context)
Expand Down
108 changes: 80 additions & 28 deletions src/android/DataCollectionPlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
import org.json.JSONException;
import org.json.JSONObject;

import android.Manifest;
import android.app.Activity;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.preference.PreferenceManager;

import com.google.android.gms.common.ConnectionResult;
Expand All @@ -37,27 +39,20 @@

public class DataCollectionPlugin extends CordovaPlugin {
public static final String TAG = "DataCollectionPlugin";
public static final int ENABLE_LOCATION_CODE = 362253;
public static String LOCATION_PERMISSION = Manifest.permission.ACCESS_FINE_LOCATION;

public static final int ENABLE_LOCATION_SETTINGS = 362253738;
public static final int ENABLE_LOCATION_PERMISSION = 362253737;

public static final String ENABLE_LOCATION_PERMISSION_ACTION = "ENABLE_LOCATION_PERMISSION";

@Override
public void pluginInitialize() {
final Activity myActivity = cordova.getActivity();
int connectionResult = GooglePlayServicesUtil.isGooglePlayServicesAvailable(myActivity);
if (connectionResult == ConnectionResult.SUCCESS) {
Log.d(myActivity, TAG, "google play services available, initializing state machine");
cordova.getThreadPool().execute(new Runnable() {
@Override
public void run() {
TripDiaryStateMachineReceiver.initOnUpgrade(myActivity);
}
});
} else {
Log.e(myActivity, TAG, "unable to connect to google play services");
NotificationHelper.createNotification(myActivity, Constants.TRACKING_ERROR_ID,
"Unable to connect to google play services, tracking turned off");
}
BuiltinUserCache.getDatabase(myActivity).putMessage(R.string.key_usercache_client_nav_event,
new StatsEvent(myActivity, R.string.app_launched));

TripDiaryStateMachineReceiver.initOnUpgrade(myActivity);
TripDiaryStateMachineReceiver.startForegroundIfNeeded(myActivity);
}

Expand All @@ -74,7 +69,10 @@ public boolean execute(String action, JSONArray data, final CallbackContext call
ConsentConfig cfg = new Gson().fromJson(newConsent.toString(), ConsentConfig.class);
ConfigManager.setConsented(ctxt, cfg);
// Now, really initialize the state machine
TripDiaryStateMachineReceiver.initOnUpgrade(ctxt);
// Note that we don't call initOnUpgrade so that we can handle the case where the
// user deleted the consent and re-consented, but didn't upgrade the app
checkAndPromptPermissions();
// ctxt.sendBroadcast(new ExplicitIntent(ctxt, R.string.transition_initialize));
// TripDiaryStateMachineReceiver.restartCollection(ctxt);
callbackContext.success();
return true;
Expand Down Expand Up @@ -109,7 +107,7 @@ public void run() {
Map<String, String> transitionMap = getTransitionMap(ctxt);
if (transitionMap.containsKey(generalTransition)) {
String androidTransition = transitionMap.get(generalTransition);
ctxt.sendBroadcast(new Intent(androidTransition));
ctxt.sendBroadcast(new ExplicitIntent(ctxt, androidTransition));
callbackContext.success(androidTransition);
} else {
callbackContext.error(generalTransition + " not supported, ignoring");
Expand Down Expand Up @@ -142,32 +140,86 @@ private static Map<String, String> getTransitionMap(Context ctxt) {
return retVal;
}

@Override
public void onNewIntent(Intent intent) {
Log.d(cordova.getActivity(), TAG, "onNewIntent(" + intent.getDataString() + ")");
Log.d(cordova.getActivity(), TAG, "Found extras " + intent.getExtras());
PendingIntent piFromIntent = intent.getParcelableExtra(NotificationHelper.RESOLUTION_PENDING_INTENT_KEY);
if (piFromIntent != null) {
private void checkAndPromptPermissions() {
if(cordova.hasPermission(LOCATION_PERMISSION)) {
TripDiaryStateMachineService.restartFSMIfStartState(cordova.getActivity());
} else {
cordova.requestPermission(this, ENABLE_LOCATION_PERMISSION, LOCATION_PERMISSION);
}
}

private void displayResolution(PendingIntent resolution) {
if (resolution != null) {
try {
// cordova.setActivityResultCallback(this);
cordova.getActivity().startIntentSenderForResult(piFromIntent.getIntentSender(), ENABLE_LOCATION_CODE, null, 0, 0, 0, null);
cordova.setActivityResultCallback(this);
cordova.getActivity().startIntentSenderForResult(resolution.getIntentSender(), ENABLE_LOCATION_SETTINGS, null, 0, 0, 0, null);
} catch (IntentSender.SendIntentException e) {
NotificationHelper.createNotification(cordova.getActivity(), Constants.TRACKING_ERROR_ID, "Unable to resolve issue");
}
}
}

/*
@Override
public void onNewIntent(Intent intent) {
Context mAct = cordova.getActivity();
Log.d(mAct, TAG, "onNewIntent(" + intent.getAction() + ")");
Log.d(mAct, TAG, "Found extras " + intent.getExtras());

if(ENABLE_LOCATION_PERMISSION_ACTION.equals(intent.getAction())) {
checkAndPromptPermissions();
return;
}
if (NotificationHelper.DISPLAY_RESOLUTION_ACTION.equals(intent.getAction())) {
PendingIntent piFromIntent = intent.getParcelableExtra(
NotificationHelper.RESOLUTION_PENDING_INTENT_KEY);
displayResolution(piFromIntent);
return;
}
Log.i(mAct, TAG, "Action "+intent.getAction()+" unknown, ignoring ");
}

@Override
public void onRequestPermissionResult(int requestCode, String[] permissions,
int[] grantResults) throws JSONException
{
/*
Let us figure out if we want to sent a javascript callback with the error.
This is currently only called from markConsented, and I don't think we listen to failures there
for(int r:grantResults)
{
if(r == PackageManager.PERMISSION_DENIED)
{
this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, PERMISSION_DENIED_ERROR));
return;
}
}
*/
switch(requestCode)
{
case ENABLE_LOCATION_PERMISSION:
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
NotificationHelper.cancelNotification(cordova.getActivity(), ENABLE_LOCATION_PERMISSION);
TripDiaryStateMachineService.restartFSMIfStartState(cordova.getActivity());
} else if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
TripDiaryStateMachineService.generateLocationEnableNotification(cordova.getActivity());
}
break;
default:
Log.e(cordova.getActivity(), TAG, "Unknown permission code "+requestCode+" ignoring");
}
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(cordova.getActivity(), TAG, "received onActivityResult("+requestCode+","+
resultCode+","+data.getDataString()+")");
switch (requestCode) {
case ENABLE_LOCATION_CODE:
case 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);
Log.d(cordova.getActivity(), TAG, "at this point, isLocationUsable = "+states.isLocationUsable());
switch (resultCode) {
case Activity.RESULT_OK:
// All required changes were successfully made
Expand All @@ -180,13 +232,13 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.e(cordova.getActivity(), TAG, "User chose not to change settings, dunno what to do");
break;
default:
Log.e(cordova.getActivity(), TAG, "Unknown result code while enabling location "+resultCode);
break;
}
break;
default:
Log.d(cordova.getActivity(), TAG, "Got unsupported request code "+requestCode+ " , ignoring...");
}
}
*/

}
28 changes: 28 additions & 0 deletions src/android/ExplicitIntent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package edu.berkeley.eecs.emission.cordova.tracker;

import android.content.Context;
import android.content.Intent;

/*
* After API 26, android does not support delivery of implicit intents.
* We use broadcast intents extensively in the FSM, so instead of changing every location,
* we create an explicit intent that sets the package name correctly and converts it to an implicit intent.
* And while we are here, we can also simplify the arguments passed in to the interface
*/

public class ExplicitIntent extends Intent {
public ExplicitIntent(Context context, int actionId) {
super(context.getString(actionId));
setPackage(context.getPackageName());
}

public ExplicitIntent(Context context, String actionString) {
super(actionString);
setPackage(context.getPackageName());
}

public ExplicitIntent(Context context, Intent intent) {
super(intent);
setPackage(context.getPackageName());
}
}
3 changes: 2 additions & 1 deletion src/android/location/GeofenceExitIntentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import android.app.IntentService;
import android.content.Intent;

import edu.berkeley.eecs.emission.cordova.tracker.ExplicitIntent;
import edu.berkeley.eecs.emission.cordova.unifiedlogger.Log;
import edu.berkeley.eecs.emission.cordova.usercache.UserCacheFactory;
import edu.berkeley.eecs.emission.cordova.tracker.wrapper.SimpleLocation;
Expand Down Expand Up @@ -60,7 +61,7 @@ protected void onHandleIntent(Intent intent) {
// in case we need it on the other side.
// intent.setAction(getString(R.string.transition_exited_geofence));
// sendBroadcast(intent);
sendBroadcast(new Intent(getString(R.string.transition_exited_geofence)));
sendBroadcast(new ExplicitIntent(this, R.string.transition_exited_geofence));
} else if (parsedEvent.getGeofenceTransition() == -1) {
// This must be a location services on/off transition
// https://github.com/e-mission/e-mission-data-collection/issues/128#issuecomment-250304943
Expand Down
4 changes: 3 additions & 1 deletion src/android/location/LocationChangeIntentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import edu.berkeley.eecs.emission.cordova.tracker.ConfigManager;
import edu.berkeley.eecs.emission.cordova.tracker.Constants;
import edu.berkeley.eecs.emission.R;

import edu.berkeley.eecs.emission.cordova.tracker.ExplicitIntent;
import edu.berkeley.eecs.emission.cordova.tracker.sensors.PollSensorManager;
import android.app.IntentService;
import android.content.Intent;
Expand Down Expand Up @@ -161,7 +163,7 @@ protected void onHandleIntent(Intent intent) {
Intent stopMonitoringIntent = new Intent();
stopMonitoringIntent.setAction(getString(R.string.transition_stopped_moving));
stopMonitoringIntent.putExtra(FusedLocationProviderApi.KEY_LOCATION_CHANGED, loc);
sendBroadcast(stopMonitoringIntent);
sendBroadcast(new ExplicitIntent(this, stopMonitoringIntent));
Log.d(this, TAG, "Finished broadcasting state change to receiver, ending trip now");
// DataUtils.endTrip(this);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
package edu.berkeley.eecs.emission.cordova.tracker.location;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.support.annotation.Nullable;

import de.appplant.cordova.plugin.notification.ClickActivity;
import edu.berkeley.eecs.emission.MainActivity;

import edu.berkeley.eecs.emission.cordova.unifiedlogger.Log;
import edu.berkeley.eecs.emission.cordova.unifiedlogger.NotificationHelper;

Expand All @@ -35,8 +38,9 @@ public int onStartCommand(Intent intent, int flags, int startId) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
Log.d(this, TAG, "onStartCommand called on oreo+, starting foreground service");
// Go to the foreground with a dummy notification
NotificationManager nMgr = (NotificationManager)this.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder builder = NotificationHelper.getNotificationBuilderForApp(this,
"background trip tracking started");
nMgr, "background trip tracking started");
builder.setOngoing(true);

Intent activityIntent = new Intent(this, MainActivity.class);
Expand Down
Loading

0 comments on commit 166ae87

Please sign in to comment.