Skip to content

Instantly share code, notes, and snippets.

@JonnyBurger
Created May 18, 2019 16:17
Show Gist options
  • Save JonnyBurger/293d73770b587a78cd85609af0adca08 to your computer and use it in GitHub Desktop.
Save JonnyBurger/293d73770b587a78cd85609af0adca08 to your computer and use it in GitHub Desktop.
diff --git a/android/app/src/main/java/host/exp/exponent/GeofencingTaskConsumer.java b/android/app/src/main/java/host/exp/exponent/GeofencingTaskConsumer.java
index b6909fd..076f760 100644
--- a/android/app/src/main/java/host/exp/exponent/GeofencingTaskConsumer.java
+++ b/android/app/src/main/java/host/exp/exponent/GeofencingTaskConsumer.java
@@ -17,6 +17,7 @@
import com.google.android.gms.location.LocationServices;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -24,6 +25,8 @@
public class GeofencingTaskConsumer extends TaskConsumer implements TaskConsumerInterface {
+ public static int VERSION = 1;
+
private static final String TAG = "GeofencingTaskConsumer";
private TaskInterface mTask;
@@ -99,22 +102,25 @@ public void didReceiveBroadcast(Intent intent) {
data.putPersistableBundle("region", region);
Context context = getContext().getApplicationContext();
- getTaskManagerUtils().scheduleJob(context, mTask, data);
+ getTaskManagerUtils().scheduleJob(context, mTask, Collections.singletonList(data));
}
}
}
@Override
public boolean didExecuteJob(JobService jobService, JobParameters params) {
- PersistableBundle data = params.getExtras().getPersistableBundle("data");
- Bundle bundle = new Bundle();
- Bundle region = new Bundle();
+ List<PersistableBundle> data = getTaskManagerUtils().extractDataFromJobParams(params);
+
+ for (PersistableBundle item : data) {
+ Bundle bundle = new Bundle();
+ Bundle region = new Bundle();
- region.putAll(data.getPersistableBundle("region"));
- bundle.putInt("eventType", data.getInt("eventType"));
- bundle.putBundle("region", region);
+ region.putAll(item.getPersistableBundle("region"));
+ bundle.putInt("eventType", item.getInt("eventType"));
+ bundle.putBundle("region", region);
- mTask.execute(bundle, null);
+ mTask.execute(bundle, null);
+ }
return true;
}
@@ -255,4 +261,4 @@ private static String getErrorString(int errorCode) {
}
//endregion
-}
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/host/exp/exponent/LocationModule.java b/android/app/src/main/java/host/exp/exponent/LocationModule.java
index 2b40b64..a8b38da 100644
--- a/android/app/src/main/java/host/exp/exponent/LocationModule.java
+++ b/android/app/src/main/java/host/exp/exponent/LocationModule.java
@@ -116,9 +116,6 @@ public String getName() {
@Override
public void setModuleRegistry(ModuleRegistry moduleRegistry) {
- if (mUIManager != null) {
- //mUIManager.unregisterLifecycleEventListener(this);
- }
mEventEmitter = moduleRegistry.getModule(EventEmitter.class);
mUIManager = moduleRegistry.getModule(UIManager.class);
@@ -126,9 +123,6 @@ public void setModuleRegistry(ModuleRegistry moduleRegistry) {
mTaskManager = moduleRegistry.getModule(TaskManagerInterface.class);
mActivityProvider = moduleRegistry.getModule(ActivityProvider.class);
- if (mUIManager != null) {
- //mUIManager.registerLifecycleEventListener(this);
- }
}
//region Expo methods
@@ -139,7 +133,6 @@ public void getCurrentPositionAsync(final Map<String, Object> options, final Pro
final Long timeout = options.containsKey("timeout") ? ((Double) options.get("timeout")).longValue() : null;
final LocationRequest locationRequest = LocationHelpers.prepareLocationRequest(options);
boolean showUserSettingsDialog = !options.containsKey(SHOW_USER_SETTINGS_DIALOG_KEY) || (boolean) options.get(SHOW_USER_SETTINGS_DIALOG_KEY);
- final int promiseDone = 0;
// Check for permissions
if (isMissingPermissions()) {
@@ -151,9 +144,7 @@ public void getCurrentPositionAsync(final Map<String, Object> options, final Pro
timeoutObject.onTimeout(new TimeoutObject.TimeoutListener() {
@Override
public void onTimeout() {
- if (promiseDone == 0) {
- promise.reject(new LocationRequestTimeoutException());
- }
+ promise.reject(new LocationRequestTimeoutException());
}
});
timeoutObject.start();
@@ -166,9 +157,7 @@ public void onTimeout() {
@Override
public void onSuccess(Location location) {
if (location != null) {
- if (promiseDone == 0) {
- promise.resolve(LocationHelpers.locationToBundle(location, Bundle.class));
- }
+ promise.resolve(LocationHelpers.locationToBundle(location, Bundle.class));
timeoutObject.markDoneIfNotTimedOut();
}
}
@@ -176,9 +165,7 @@ public void onSuccess(Location location) {
}
if (LocationHelpers.hasNetworkProviderEnabled(mContext) || !showUserSettingsDialog) {
- if (promiseDone == 0) {
- LocationHelpers.requestSingleLocation(this, locationRequest, timeoutObject, promise);
- }
+ LocationHelpers.requestSingleLocation(this, locationRequest, timeoutObject, promise);
} else {
// Pending requests can ask the user to turn on improved accuracy mode in user's settings.
addPendingLocationRequest(locationRequest, new LocationActivityResultListener() {
@@ -559,8 +546,6 @@ public void onFailure(@NonNull Exception e) {
try {
ResolvableApiException resolvable = (ResolvableApiException) e;
-
- //mUIManager.registerActivityEventListener(LocationModule.this);
resolvable.startResolutionForResult(activity, CHECK_SETTINGS_REQUEST_CODE);
} catch (IntentSender.SendIntentException sendEx) {
// Ignore the error.
@@ -770,7 +755,6 @@ public void onActivityResult(Activity activity, int requestCode, int resultCode,
return;
}
executePendingRequests(resultCode);
- //mUIManager.unregisterLifecycleEventListener(this);
}
@Override
@@ -798,4 +782,4 @@ public void onHostDestroy() {
}
//endregion
-}
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/host/exp/exponent/LocationTaskConsumer.java b/android/app/src/main/java/host/exp/exponent/LocationTaskConsumer.java
index a5cf74b..254bce4 100644
--- a/android/app/src/main/java/host/exp/exponent/LocationTaskConsumer.java
+++ b/android/app/src/main/java/host/exp/exponent/LocationTaskConsumer.java
@@ -21,6 +21,10 @@
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
+import io.nlopez.smartlocation.location.config.LocationParams;
+
+
+import expo.modules.location.LocationHelpers;
import java.util.ArrayList;
import java.util.Collections;
@@ -28,20 +32,14 @@
import java.util.Map;
-public class LocationTaskConsumer extends TaskConsumer implements TaskConsumerInterface, LifecycleEventListener {
+public class LocationTaskConsumer extends TaskConsumer implements TaskConsumerInterface {
private static final String TAG = "LocationTaskConsumer";
- private static final String FOREGROUND_SERVICE_KEY = "foregroundService";
private static long sLastTimestamp = 0;
private TaskInterface mTask;
private PendingIntent mPendingIntent;
- private LocationTaskService mService;
private LocationRequest mLocationRequest;
private FusedLocationProviderClient mLocationClient;
- private Location mLastReportedLocation;
- private double mDeferredDistance = 0.0;
- private List<Location> mDeferredLocations = new ArrayList<>();
- private boolean mIsHostPaused = true;
public LocationTaskConsumer(Context context, TaskManagerUtilsInterface taskManagerUtils) {
super(context, taskManagerUtils);
@@ -57,13 +55,11 @@ public String taskType() {
public void didRegister(TaskInterface task) {
mTask = task;
startLocationUpdates();
- maybeStartForegroundService();
}
@Override
public void didUnregister() {
stopLocationUpdates();
- stopForegroundService();
mTask = null;
mPendingIntent = null;
mLocationRequest = null;
@@ -73,13 +69,8 @@ public void didUnregister() {
@Override
public void setOptions(Map<String, Object> options) {
super.setOptions(options);
-
- // Restart location updates
stopLocationUpdates();
startLocationUpdates();
-
- // Restart foreground service if its option has changed.
- maybeStartForegroundService();
}
@Override
@@ -88,54 +79,53 @@ public void didReceiveBroadcast(Intent intent) {
return;
}
+ Context context = getContext().getApplicationContext();
LocationResult result = LocationResult.extractResult(intent);
if (result != null) {
+ List<PersistableBundle> data = new ArrayList<>();
List<Location> locations = result.getLocations();
- deferLocations(locations);
- maybeReportDeferredLocations();
- } else {
- try {
- mLocationClient.getLastLocation().addOnCompleteListener(new OnCompleteListener<Location>() {
- @Override
- public void onComplete(@NonNull Task<Location> task) {
- Location location = task.getResult();
-
- if (location != null) {
- Log.i(TAG, "get last location: " + location);
- deferLocations(Collections.singletonList(location));
- maybeReportDeferredLocations();
- }
- }
- });
- } catch (SecurityException e) {
- Log.e(TAG, "Cannot get last location: " + e.getMessage());
+ for (Location location : locations) {
+ long timestamp = location.getTime();
+
+ // Some devices may broadcast the same location multiple times (mostly twice) so we're filtering out these locations,
+ // so only one location at the specific timestamp can schedule a job.
+ if (timestamp > sLastTimestamp) {
+ PersistableBundle bundle = host.exp.exponent.LocationHelpers.locationToBundle(location, PersistableBundle.class);
+
+ data.add(bundle);
+ sLastTimestamp = timestamp;
+ }
+ }
+
+ if (data.size() > 0) {
+ // Schedule new job.
+ getTaskManagerUtils().scheduleJob(context, mTask, data);
}
}
}
@Override
public boolean didExecuteJob(final JobService jobService, final JobParameters params) {
- PersistableBundle data = params.getExtras().getPersistableBundle("data");
- int length = data != null ? data.getInt("length", 0) : 0;
+ List<PersistableBundle> data = getTaskManagerUtils().extractDataFromJobParams(params);
+ Bundle bundleData = new Bundle();
ArrayList<Bundle> locationBundles = new ArrayList<>();
- for (int i = 0; i < length; i++) {
- PersistableBundle persistableLocationBundle = data.getPersistableBundle(String.valueOf(i));
+ for (PersistableBundle persistableLocationBundle : data) {
Bundle locationBundle = new Bundle();
Bundle coordsBundle = new Bundle();
- if (persistableLocationBundle != null) {
- coordsBundle.putAll(persistableLocationBundle.getPersistableBundle("coords"));
- locationBundle.putAll(persistableLocationBundle);
- locationBundle.putBundle("coords", coordsBundle);
+ coordsBundle.putAll(persistableLocationBundle.getPersistableBundle("coords"));
+ locationBundle.putAll(persistableLocationBundle);
+ locationBundle.putBundle("coords", coordsBundle);
- locationBundles.add(locationBundle);
- }
+ locationBundles.add(locationBundle);
}
- executeTaskWithLocationBundles(locationBundles, new TaskExecutionCallback() {
+ bundleData.putParcelableArrayList("locations", locationBundles);
+
+ mTask.execute(bundleData, null, new TaskExecutionCallback() {
@Override
public void onFinished(Map<String, Object> response) {
jobService.jobFinished(params, false);
@@ -161,7 +151,7 @@ private void startLocationUpdates() {
return;
}
- mLocationRequest = LocationHelpers.prepareLocationRequest(mTask.getOptions());
+ mLocationRequest = prepareLocationRequest();
mPendingIntent = preparePendingIntent();
try {
@@ -179,166 +169,37 @@ private void stopLocationUpdates() {
}
}
- private void maybeStartForegroundService() {
- // Foreground service is available as of Android Oreo.
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
- return;
- }
-
- ReadableArguments options = new MapArguments(mTask.getOptions());
- final Context context = getContext();
- boolean useForegroundService = options.containsKey(FOREGROUND_SERVICE_KEY);
-
- if (context == null) {
- Log.w(TAG, "Context not found when trying to start foreground service.");
- return;
- }
-
- // Service is already running, but the task has been registered again without `foregroundService` option.
- if (mService != null && !useForegroundService) {
- stopForegroundService();
- return;
- }
-
- // Service is not running and the user don't want to start foreground service.
- if (!useForegroundService) {
- return;
- }
-
- // Foreground service is requested but not running.
- if (mService == null) {
- Intent serviceIntent = new Intent(context, LocationTaskService.class);
- Bundle extras = new Bundle();
- final Bundle serviceOptions = options.getArguments(FOREGROUND_SERVICE_KEY).toBundle();
-
- extras.putString("appId", mTask.getAppId());
- extras.putString("taskName", mTask.getName());
- serviceIntent.putExtras(extras);
-
- context.startForegroundService(serviceIntent);
-
- context.bindService(serviceIntent, new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mService = ((LocationTaskService.ServiceBinder) service).getService();
- mService.setParentContext(context);
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- mService.stop();
- mService = null;
- }
- }, Context.BIND_AUTO_CREATE);
- } else {
- // Restart the service with new service options.
- mService.startForeground();
- }
- }
-
- private void stopForegroundService() {
- if (mService != null) {
- mService.stop();
- }
- }
-
- private void deferLocations(List<Location> locations) {
- int size = mDeferredLocations.size();
- Location lastLocation = size > 0 ? mDeferredLocations.get(size - 1) : mLastReportedLocation;
+ private LocationRequest prepareLocationRequest() {
+ Map<String, Object> options = mTask.getOptions();
+ LocationParams locationParams = LocationHelpers.mapOptionsToLocationParams(options);
+ int accuracy = LocationHelpers.getAccuracyFromOptions(options);
- for (Location location : locations) {
- if (lastLocation != null) {
- mDeferredDistance += Math.abs(location.distanceTo(lastLocation));
- }
- lastLocation = location;
- }
- mDeferredLocations.addAll(locations);
- }
-
- private void maybeReportDeferredLocations() {
- if (!shouldReportDeferredLocations()) {
- // Don't report locations yet - continue deferring them.
- return;
- }
-
- Context context = getContext().getApplicationContext();
- PersistableBundle data = new PersistableBundle();
- int length = 0;
-
- for (Location location : mDeferredLocations) {
- long timestamp = location.getTime();
-
- // Some devices may broadcast the same location multiple times (mostly twice) so we're filtering out these locations,
- // so only one location at the specific timestamp can schedule a job.
- if (timestamp > sLastTimestamp) {
- PersistableBundle bundle = LocationHelpers.locationToBundle(location, PersistableBundle.class);
- data.putPersistableBundle(Integer.valueOf(length).toString(), bundle);
-
- sLastTimestamp = timestamp;
- ++length;
- }
- }
-
- data.putInt("length", length);
-
- if (length > 0) {
- // Save last reported location, reset the distance and clear a list of locations.
- mLastReportedLocation = mDeferredLocations.get(mDeferredLocations.size() - 1);
- mDeferredDistance = 0.0;
- mDeferredLocations.clear();
-
- // Schedule new job.
- getTaskManagerUtils().scheduleJob(context, mTask, data);
- }
- }
-
- private boolean shouldReportDeferredLocations() {
- if (mDeferredLocations.size() == 0) {
- return false;
- }
- if (!mIsHostPaused) {
- // Don't defer location updates when the activity is in foreground state.
- return true;
- }
-
- Location oldestLocation = mLastReportedLocation != null ? mLastReportedLocation : mDeferredLocations.get(0);
- Location newestLocation = mDeferredLocations.get(mDeferredLocations.size() - 1);
- Arguments options = new MapHelper(mTask.getOptions());
- double distance = options.getDouble("deferredUpdatesDistance");
- long interval = options.getLong("deferredUpdatesInterval");
-
- return newestLocation.getTime() - oldestLocation.getTime() >= interval && mDeferredDistance >= distance;
+ return new LocationRequest()
+ .setFastestInterval(locationParams.getInterval())
+ .setInterval(locationParams.getInterval())
+ .setSmallestDisplacement(locationParams.getDistance())
+ .setPriority(mapAccuracyToPriority(accuracy));
}
private PendingIntent preparePendingIntent() {
return getTaskManagerUtils().createTaskIntent(getContext(), mTask);
}
- private void executeTaskWithLocationBundles(ArrayList<Bundle> locationBundles, TaskExecutionCallback callback) {
- if (locationBundles.size() > 0) {
- Bundle data = new Bundle();
- data.putParcelableArrayList("locations", locationBundles);
- mTask.execute(data, null, callback);
- } else {
- callback.onFinished(null);
+ private int mapAccuracyToPriority(int accuracy) {
+ switch (accuracy) {
+ case LocationModule.ACCURACY_BEST_FOR_NAVIGATION:
+ case LocationModule.ACCURACY_HIGHEST:
+ case LocationModule.ACCURACY_HIGH:
+ return LocationRequest.PRIORITY_HIGH_ACCURACY;
+ case LocationModule.ACCURACY_BALANCED:
+ default:
+ return LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY;
+ case LocationModule.ACCURACY_LOW:
+ return LocationRequest.PRIORITY_LOW_POWER;
+ case LocationModule.ACCURACY_LOWEST:
+ return LocationRequest.PRIORITY_NO_POWER;
}
}
- @Override
- public void onHostResume() {
- mIsHostPaused = false;
- maybeReportDeferredLocations();
- }
-
- @Override
- public void onHostPause() {
- mIsHostPaused = true;
- }
-
- @Override
- public void onHostDestroy() {
- mIsHostPaused = true;
- }
-
//endregion
}
\ No newline at end of file
diff --git a/android/app/src/main/java/host/exp/exponent/TaskConsumerInterface.java b/android/app/src/main/java/host/exp/exponent/TaskConsumerInterface.java
index 6b3d655..322e31a 100644
--- a/android/app/src/main/java/host/exp/exponent/TaskConsumerInterface.java
+++ b/android/app/src/main/java/host/exp/exponent/TaskConsumerInterface.java
@@ -38,5 +38,9 @@
*/
boolean didCancelJob(JobService jobService, JobParameters params);
+ /**
+ * Called when registering already registered task with different options.
+ */
void setOptions(Map<String, Object> options);
-}
+
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/host/exp/exponent/TaskManagerInternalModule.java b/android/app/src/main/java/host/exp/exponent/TaskManagerInternalModule.java
index 0395931..5fbfc34 100644
--- a/android/app/src/main/java/host/exp/exponent/TaskManagerInternalModule.java
+++ b/android/app/src/main/java/host/exp/exponent/TaskManagerInternalModule.java
@@ -63,7 +63,7 @@ public void unregisterTask(String taskName, Class consumerClass) throws Exceptio
}
@Override
- public void executeTaskWithBody(Bundle body) {
+ public synchronized void executeTaskWithBody(Bundle body) {
if (mEventsQueue != null) {
// `startObserving` on TaskManagerModule wasn't called yet - add event body to the queue.
mEventsQueue.add(body);
@@ -82,7 +82,7 @@ public boolean taskHasConsumerOfClass(String taskName, Class consumerClass) {
}
@Override
- public void flushQueuedEvents() {
+ public synchronized void flushQueuedEvents() {
// Execute any events that came before this call.
if (mEventsQueue != null) {
for (Bundle body : mEventsQueue) {
diff --git a/android/app/src/main/java/host/exp/exponent/TaskManagerUtils.java b/android/app/src/main/java/host/exp/exponent/TaskManagerUtils.java
index a7dbe38..b7584ff 100644
--- a/android/app/src/main/java/host/exp/exponent/TaskManagerUtils.java
+++ b/android/app/src/main/java/host/exp/exponent/TaskManagerUtils.java
@@ -5,78 +5,234 @@
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
+import android.app.job.JobParameters;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.PersistableBundle;
+import android.support.v4.util.ArraySet;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.Collections;
+import java.util.Comparator;
public class TaskManagerUtils implements TaskManagerUtilsInterface {
+ // Key that every job created by the task manager must contain in its extras bundle.
+ private static final String EXTRAS_REQUIRED_KEY = "expo.modules.taskManager";
+
+ // Request code number used for pending intents created by this module.
+ private static final int PENDING_INTENT_REQUEST_CODE = 5055;
+
private static final int DEFAULT_OVERRIDE_DEADLINE = 60 * 1000; // 1 minute
- // make intent and job ids locally-unique
- private static Integer sCurrentIntentId = 2137;
- private static Integer sCurrentJobId = 2137;
+ private static final Set<TaskInterface> sTasksReschedulingJob = new ArraySet<>();
- // Set of job IDs that are scheduled but not started yet.
- private static final List<Integer> sPendingJobIds = new ArrayList<>();
+ //region TaskManagerUtilsInterface
+ @Override
public PendingIntent createTaskIntent(Context context, TaskInterface task) {
- Integer intentId = sCurrentIntentId++;
- String appId = task.getAppId();
- String taskName = task.getName();
+ return createTaskIntent(context, task, PendingIntent.FLAG_UPDATE_CURRENT);
+ }
+
+ @Override
+ public void cancelTaskIntent(Context context, String appId, String taskName) {
+ PendingIntent pendingIntent = createTaskIntent(context, appId, taskName, PendingIntent.FLAG_NO_CREATE);
+
+ if (pendingIntent != null) {
+ pendingIntent.cancel();
+ }
+ }
+
+ @Override
+ public void scheduleJob(Context context, TaskInterface task, List<PersistableBundle> data) {
+ updateOrScheduleJob(context, task, data);
+ }
+
+ @Override
+ public void cancelScheduledJob(Context context, int jobId) {
+ JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+ if (jobScheduler != null) {
+ jobScheduler.cancel(jobId);
+ } else {
+ Log.e(this.getClass().getName(), "Job scheduler not found!");
+ }
+ }
+
+ @Override
+ public List<PersistableBundle> extractDataFromJobParams(JobParameters params) {
+ PersistableBundle extras = params.getExtras();
+ List<PersistableBundle> data = new ArrayList<>();
+ int dataSize = extras.getInt("dataSize", 0);
+
+ for (int i = 0; i < dataSize; i++) {
+ data.add(extras.getPersistableBundle(String.valueOf(i)));
+ }
+ return data;
+ }
+
+ //endregion TaskManagerUtilsInterface
+ //region static helpers
+
+ static boolean notifyTaskJobCancelled(TaskInterface task) {
+ boolean isRescheduled = sTasksReschedulingJob.contains(task);
+
+ if (isRescheduled) {
+ sTasksReschedulingJob.remove(task);
+ }
+ return isRescheduled;
+ }
+
+ //endregion static helpers
+ //region private helpers
+
+ private void updateOrScheduleJob(Context context, TaskInterface task, List<PersistableBundle> data) {
+ JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+ if (jobScheduler == null) {
+ Log.e(this.getClass().getName(), "Job scheduler not found!");
+ return;
+ }
+
+ List<JobInfo> pendingJobs = jobScheduler.getAllPendingJobs();
+
+ Collections.sort(pendingJobs, new Comparator<JobInfo>() {
+ @Override
+ public int compare(JobInfo a, JobInfo b) {
+ return Integer.compare(a.getId(), b.getId());
+ }
+ });
+
+ // We will be looking for the lowest number that is not being used yet.
+ int newJobId = 0;
+
+ for (JobInfo jobInfo : pendingJobs) {
+ int jobId = jobInfo.getId();
+
+ if (isJobInfoRelatedToTask(jobInfo, task)) {
+ JobInfo mergedJobInfo = createJobInfoByAddingData(jobInfo, data);
+
+ // Add the task to the list of rescheduled tasks.
+ sTasksReschedulingJob.add(task);
+
+ try {
+ // Cancel jobs with the same ID to let them be rescheduled.
+ jobScheduler.cancel(jobId);
+
+ // Reschedule job for given task.
+ jobScheduler.schedule(mergedJobInfo);
+ } catch (IllegalStateException e) {
+ Log.e(this.getClass().getName(), "Unable to reschedule a job: " + e.getMessage());
+ }
+ return;
+ }
+ if (newJobId == jobId) {
+ newJobId++;
+ }
+ }
+
+ try {
+ // Given task doesn't have any pending jobs yet, create a new JobInfo and schedule it then.
+ JobInfo jobInfo = createJobInfo(context, task, newJobId, data);
+ jobScheduler.schedule(jobInfo);
+ } catch (IllegalStateException e) {
+ Log.e(this.getClass().getName(), "Unable to schedule a new job: " + e.getMessage());
+ }
+ }
+
+ private JobInfo createJobInfoByAddingData(JobInfo jobInfo, List<PersistableBundle> data) {
+ PersistableBundle mergedExtras = jobInfo.getExtras();
+ int dataSize = mergedExtras.getInt("dataSize", 0);
+
+ if (data != null) {
+ mergedExtras.putInt("dataSize", dataSize + data.size());
+
+ for (int i = 0; i < data.size(); i++) {
+ mergedExtras.putPersistableBundle(String.valueOf(dataSize + i), data.get(i));
+ }
+ }
+ return createJobInfo(jobInfo.getId(), jobInfo.getService(), mergedExtras);
+ }
+
+ private PendingIntent createTaskIntent(Context context, String appId, String taskName, int flags) {
+ if (context == null) {
+ return null;
+ }
+
Intent intent = new Intent(TaskBroadcastReceiver.INTENT_ACTION, null, context, TaskBroadcastReceiver.class);
Uri dataUri = new Uri.Builder()
- .appendQueryParameter("intentId", intentId.toString())
.appendQueryParameter("appId", appId)
.appendQueryParameter("taskName", taskName)
.build();
intent.setData(dataUri);
- return PendingIntent.getBroadcast(context, intentId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getBroadcast(context, PENDING_INTENT_REQUEST_CODE, intent, flags);
}
- public void scheduleJob(Context context, JobInfo jobInfo) {
- JobScheduler jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ private PendingIntent createTaskIntent(Context context, TaskInterface task, int flags) {
+ String appId = task.getAppId();
+ String taskName = task.getName();
- int jobId = jobInfo.getId();
+ return createTaskIntent(context, appId, taskName, flags);
+ }
- if (jobScheduler != null) {
- // Cancel jobs with the same ID to let them be rescheduled.
- jobScheduler.cancel(jobId);
+ private JobInfo createJobInfo(int jobId, ComponentName jobService, PersistableBundle extras) {
+ return new JobInfo.Builder(jobId, jobService)
+ .setExtras(extras)
+ .setMinimumLatency(0)
+ .setOverrideDeadline(DEFAULT_OVERRIDE_DEADLINE)
+ .build();
+ }
- // Schedule a job.
- try {
- jobScheduler.schedule(jobInfo);
- } catch (IllegalStateException err) {
- jobScheduler.cancelAll();
- }
- addToPendingJobs(jobId);
+ private JobInfo createJobInfo(Context context, TaskInterface task, int jobId, List<PersistableBundle> data) {
+ return createJobInfo(jobId, new ComponentName(context, TaskJobService.class), createExtrasForTask(task, data));
+ }
+
+ private PersistableBundle createExtrasForTask(TaskInterface task, List<PersistableBundle> data) {
+ PersistableBundle extras = new PersistableBundle();
+ extras.putInt(EXTRAS_REQUIRED_KEY, 1);
+ extras.putString("appId", task.getAppId());
+ extras.putString("taskName", task.getName());
+
+ if (data != null) {
+ extras.putInt("dataSize", data.size());
+
+ for (int i = 0; i < data.size(); i++) {
+ extras.putPersistableBundle(String.valueOf(i), data.get(i));
+ }
} else {
- Log.e(this.getClass().getName(), "Job scheduler not found!");
+ extras.putInt("dataSize", 0);
}
+
+ return extras;
}
- public void scheduleJob(Context context, TaskInterface task, PersistableBundle data) {
- Integer jobId = sCurrentJobId++;
- PersistableBundle extras = createExtrasForTask(task, data);
- JobInfo jobInfo = createJobInfo(context, jobId, extras);
+ private boolean isJobInfoRelatedToTask(JobInfo jobInfo, TaskInterface task) {
+ PersistableBundle extras = jobInfo.getExtras();
+ String appId = task.getAppId();
+ String taskName = task.getName();
- scheduleJob(context, jobInfo);
+ if (extras.containsKey(EXTRAS_REQUIRED_KEY)) {
+ return appId.equals(extras.getString("appId", "")) && taskName.equals(extras.getString("taskName", ""));
+ }
+ return false;
}
+ //endregion private helpers
+ //region converting map to bundle
+
@SuppressWarnings("unchecked")
- public static Bundle mapToBundle(Map<String, Object> map) {
+ static Bundle mapToBundle(Map<String, Object> map) {
Bundle bundle = new Bundle();
for (Map.Entry<String, Object> entry : map.entrySet()) {
@@ -112,7 +268,7 @@ public static Bundle mapToBundle(Map<String, Object> map) {
}
@SuppressWarnings("unchecked")
- public static double[] listToDoubleArray(List<Object> list) {
+ private static double[] listToDoubleArray(List<Object> list) {
double[] doubles = new double[list.size()];
for (int i = 0; i < list.size(); i++) {
doubles[i] = (Double) list.get(i);
@@ -149,36 +305,5 @@ public static Bundle mapToBundle(Map<String, Object> map) {
return arrayList;
}
- private static void addToPendingJobs(int jobId) {
- if (!sPendingJobIds.contains(jobId)) {
- sPendingJobIds.add(jobId);
- }
- }
-
- public static void removeFromPendingJobs(int jobId) {
- sPendingJobIds.remove((Integer) jobId);
- }
-
- //region private
-
- private JobInfo createJobInfo(Context context, int jobId, PersistableBundle extras) {
- return new JobInfo.Builder(jobId, new ComponentName(context, TaskJobService.class))
- .setExtras(extras)
- .setMinimumLatency(0)
- .setOverrideDeadline(DEFAULT_OVERRIDE_DEADLINE)
- .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
- .build();
- }
-
- private PersistableBundle createExtrasForTask(TaskInterface task, PersistableBundle data) {
- PersistableBundle extras = new PersistableBundle();
-
- extras.putString("appId", task.getAppId());
- extras.putString("taskName", task.getName());
- extras.putPersistableBundle("data", data);
-
- return extras;
- }
-
- //endregion
-}
+ //endregion converting map to bundle
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/host/exp/exponent/TaskManagerUtilsInterface.java b/android/app/src/main/java/host/exp/exponent/TaskManagerUtilsInterface.java
index 58be5d8..ea4cdc6 100644
--- a/android/app/src/main/java/host/exp/exponent/TaskManagerUtilsInterface.java
+++ b/android/app/src/main/java/host/exp/exponent/TaskManagerUtilsInterface.java
@@ -2,15 +2,35 @@
import android.app.PendingIntent;
import android.app.job.JobInfo;
+import android.app.job.JobParameters;
import android.content.Context;
import android.os.PersistableBundle;
import java.util.List;
public interface TaskManagerUtilsInterface {
+ /**
+ * Creates pending intent that represents the task containing all its params.
+ */
PendingIntent createTaskIntent(Context context, TaskInterface task);
- void scheduleJob(Context context, JobInfo jobInfoArg);
+ /**
+ * Cancels pending intent for given task.
+ */
+ void cancelTaskIntent(Context context, String appId, String taskName);
- void scheduleJob(Context context, TaskInterface task, PersistableBundle extras);
-}
+ /**
+ * Schedules a job for given task and with given list of extra data.
+ */
+ void scheduleJob(Context context, TaskInterface task, List<PersistableBundle> data);
+
+ /**
+ * Cancels scheduled job with given identifier.
+ */
+ void cancelScheduledJob(Context context, int jobId);
+
+ /**
+ * Extracts data list from job parameters.
+ */
+ List<PersistableBundle> extractDataFromJobParams(JobParameters params);
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/host/exp/exponent/TaskService.java b/android/app/src/main/java/host/exp/exponent/TaskService.java
index 293ac86..7601911 100644
--- a/android/app/src/main/java/host/exp/exponent/TaskService.java
+++ b/android/app/src/main/java/host/exp/exponent/TaskService.java
@@ -179,6 +179,7 @@ public Bundle getTaskOptions(String taskName, String appId) {
return tasks;
}
+
@Override
public void notifyTaskFinished(String taskName, final String appId, Map<String, Object> response) {
String eventId = (String) response.get("eventId");
@@ -281,9 +282,6 @@ public boolean handleJob(final JobService jobService, final JobParameters params
TaskConsumerInterface consumer = getTaskConsumer(taskName, appId);
- // remove job ID from pending jobs
- TaskManagerUtils.removeFromPendingJobs(params.getJobId());
-
if (consumer == null) {
Log.w(TAG, "Task or consumer not found.");
return false;
@@ -309,9 +307,6 @@ public boolean cancelJob(JobService jobService, JobParameters params) {
TaskConsumerInterface consumer = getTaskConsumer(taskName, appId);
- // remove job ID from pending jobs
- TaskManagerUtils.removeFromPendingJobs(params.getJobId());
-
if (consumer == null) {
return false;
}
@@ -737,4 +732,4 @@ private static Class unversionedClassForClass(Class versionedClass) {
}
//endregion
-}
+}
\ No newline at end of file
diff --git a/android/app/src/main/java/host/exp/exponent/generated/DetachBuildConstants.java b/android/app/src/main/java/host/exp/exponent/generated/DetachBuildConstants.java
index d07e432..7294b2e 100644
--- a/android/app/src/main/java/host/exp/exponent/generated/DetachBuildConstants.java
+++ b/android/app/src/main/java/host/exp/exponent/generated/DetachBuildConstants.java
@@ -3,6 +3,6 @@
// This file is auto-generated. Please don't rename!
public class DetachBuildConstants {
- public static final String DEVELOPMENT_URL = "exp87235a6a39874b039c6d0aa017fee548://192.168.0.187:19000";
+ public static final String DEVELOPMENT_URL = "exp87235a6a39874b039c6d0aa017fee548://192.168.0.186:19000";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment