if (JobApi.WORK_MANAGER.isSupported(this)) { menu.findItem(R.id.action_force_work).setChecked(false); } else { menu.findItem(R.id.action_force_work).setVisible(false); if (JobApi.V_26.isSupported(this)) { menu.findItem(R.id.action_force_26).setChecked(false); } else { menu.findItem(R.id.action_force_26).setVisible(false); if (JobApi.V_24.isSupported(this)) { menu.findItem(R.id.action_force_24).setChecked(false); } else { menu.findItem(R.id.action_force_24).setVisible(false); if (JobApi.V_21.isSupported(this)) { menu.findItem(R.id.action_force_21).setChecked(false); } else { menu.findItem(R.id.action_force_21).setVisible(false); if (JobApi.V_19.isSupported(this)) { menu.findItem(R.id.action_force_19).setChecked(false); } else { menu.findItem(R.id.action_force_19).setVisible(false); if (JobApi.V_14.isSupported(this)) { menu.findItem(R.id.action_force_14).setChecked(false); } else { menu.findItem(R.id.action_force_14).setVisible(false);
/*package*/ static void cleanUpOrphanedJob(Context context, int jobId) { /* * That's necessary if the database was deleted and jobs (especially the JobScheduler) are still around. * Then if a new job is being scheduled, it's possible that the new job has the ID of the old one. Here * we make sure, that no job is left in the system. */ for (JobApi jobApi : JobApi.values()) { if (jobApi.isSupported(context)) { try { jobApi.getProxy(context).cancel(jobId); } catch (Exception ignored) { // GCM API could crash if it's disabled, ignore crashes at this point and continue } } } }
@NonNull /*package*/ synchronized JobProxy getProxy(Context context) { if (mCachedProxy == null) { mCachedProxy = createProxy(context); } return mCachedProxy; }
/*package*/ void destroy() { synchronized (JobManager.class) { instance = null; for (JobApi api : JobApi.values()) { api.invalidateCachedProxy(); } } }
public boolean isSupported(Context context) { switch (this) { case WORK_MANAGER: return WorkManagerAvailableHelper.isWorkManagerApiSupported(); case V_26: return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isServiceEnabled(context, PlatformJobService.class); case V_24: return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && isServiceEnabledAndHasPermission(context, PlatformJobService.class, JOB_SCHEDULER_PERMISSION); case V_21: return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isServiceEnabledAndHasPermission(context, PlatformJobService.class, JOB_SCHEDULER_PERMISSION); case V_19: return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && isServiceEnabled(context, PlatformAlarmService.class) && isBroadcastEnabled(context, PlatformAlarmReceiver.class); case V_14: return JobConfig.isForceAllowApi14() || (isServiceEnabled(context, PlatformAlarmService.class) && isServiceEnabled(context, PlatformAlarmServiceExact.class) && isBroadcastEnabled(context, PlatformAlarmReceiver.class)); case GCM: try { // see https://github.com/evernote/android-job/issues/487 return GcmAvailableHelper.isGcmApiSupported(context); } catch (Exception e) { return false; } default: throw new IllegalStateException("not implemented"); } }
boolean flexSupport = periodic && jobApi.isFlexSupport() && request.getFlexMs() < request.getIntervalMs(); jobApi.invalidateCachedProxy(); throw e; } else { jobApi = JobApi.V_19.isSupported(mContext) ? JobApi.V_19 : JobApi.V_14; // try one last time
private void testAllImpl() { for (JobApi api : JobApi.values()) { if (api.isSupported(this)) { JobConfig.forceApi(api); testSimple(); } else { Log.w("Demo", String.format("%s is not supported", api)); } } JobConfig.reset(); }
/*package*/ JobApi getJobApi() { return mBuilder.mExact ? JobApi.V_14 : JobApi.getDefault(context()); }
/** * <b>WARNING:</b> You shouldn't call this method. It only exists for testing and debugging * purposes. The {@link JobManager} automatically decides which API suits best for a {@link Job}. * * @param api The {@link JobApi} which will be used for future scheduled JobRequests. */ public static void forceApi(@NonNull JobApi api) { for (JobApi jobApi : JobApi.values()) { ENABLED_APIS.put(jobApi, jobApi == api); } CAT.w("forceApi - %s", api); }
@NonNull public static JobApi getDefault(Context context) { if (WORK_MANAGER.isSupported(context) && JobConfig.isApiEnabled(WORK_MANAGER)) { return WORK_MANAGER; } else if (V_26.isSupported(context) && JobConfig.isApiEnabled(V_26)) { return V_26; } else if (V_24.isSupported(context) && JobConfig.isApiEnabled(V_24)) { return V_24; } else if (V_21.isSupported(context) && JobConfig.isApiEnabled(V_21)) { return V_21; } else if (GCM.isSupported(context) && JobConfig.isApiEnabled(GCM)) { return GCM; } else if (V_19.isSupported(context) && JobConfig.isApiEnabled(V_19)) { return V_19; } else if (JobConfig.isApiEnabled(V_14)) { return V_14; } else { throw new IllegalStateException("All supported APIs are disabled"); } } }
/*package*/ JobProxy getJobProxy(JobApi api) { return api.getProxy(mContext); }
boolean flexSupport = periodic && jobApi.isFlexSupport() && request.getFlexMs() < request.getIntervalMs(); jobApi.invalidateCachedProxy(); throw e; } else { jobApi = JobApi.V_19.isSupported(mContext) ? JobApi.V_19 : JobApi.V_14; // try one last time
@Test @Config(sdk = Build.VERSION_CODES.O) public void verifyApi26Supported() { assertThat(JobApi.getDefault(context())).isEqualTo(JobApi.V_26); }
/** * Resets all adjustments in the config. */ public static void reset() { for (JobApi api : JobApi.values()) { ENABLED_APIS.put(api, Boolean.TRUE); } allowSmallerIntervals = false; forceAllowApi14 = false; jobReschedulePause = DEFAULT_JOB_RESCHEDULE_PAUSE; skipJobReschedule = false; jobIdOffset = 0; forceRtc = false; clock = Clock.DEFAULT; executorService = DEFAULT_EXECUTOR_SERVICE; closeDatabase = false; JobCat.setLogcatEnabled(true); JobCat.clearLogger(); } }
@Test public void verifyJobApiNotSupportedWhenServiceIsDisabled() { assumeTrue(Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { assertThat(JobApi.V_26.isSupported(mContext)).isTrue(); } assertThat(JobApi.V_24.isSupported(mContext)).isTrue(); assertThat(JobApi.V_21.isSupported(mContext)).isTrue(); mPackageManager.setComponentEnabledSetting(mComponent, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); assertThat(JobApi.V_26.isSupported(mContext)).isFalse(); assertThat(JobApi.V_24.isSupported(mContext)).isFalse(); assertThat(JobApi.V_21.isSupported(mContext)).isFalse(); } }
/*package*/ void destroy() { synchronized (JobManager.class) { instance = null; for (JobApi api : JobApi.values()) { api.invalidateCachedProxy(); } } }
public boolean isSupported(Context context) { switch (this) { case V_26: return Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isServiceEnabled(context, PlatformJobService.class); case V_24: return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && isServiceEnabledAndHasPermission(context, PlatformJobService.class, JOB_SCHEDULER_PERMISSION); case V_21: return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && isServiceEnabledAndHasPermission(context, PlatformJobService.class, JOB_SCHEDULER_PERMISSION); case V_19: return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && isServiceEnabled(context, PlatformAlarmService.class) && isBroadcastEnabled(context, PlatformAlarmReceiver.class); case V_14: return JobConfig.isForceAllowApi14() || (isServiceEnabled(context, PlatformAlarmService.class) && isServiceEnabled(context, PlatformAlarmServiceExact.class) && isBroadcastEnabled(context, PlatformAlarmReceiver.class)); case GCM: return GcmAvailableHelper.isGcmApiSupported(context); default: throw new IllegalStateException("not implemented"); } }
/*package*/ Set<JobRequest> getAllJobRequests(@Nullable String tag, boolean includeStarted, boolean cleanUpTransient) { Set<JobRequest> requests = getJobStorage().getAllJobRequests(tag, includeStarted); if (cleanUpTransient) { Iterator<JobRequest> iterator = requests.iterator(); while (iterator.hasNext()) { JobRequest request = iterator.next(); if (request.isTransient() && !request.getJobApi().getProxy(mContext).isPlatformJobScheduled(request)) { getJobStorage().remove(request); iterator.remove(); } } } return requests; }
/*package*/ static void cleanUpOrphanedJob(Context context, int jobId) { /* * That's necessary if the database was deleted and jobs (especially the JobScheduler) are still around. * Then if a new job is being scheduled, it's possible that the new job has the ID of the old one. Here * we make sure, that no job is left in the system. */ for (JobApi jobApi : JobApi.values()) { if (jobApi.isSupported(context)) { try { jobApi.getProxy(context).cancel(jobId); } catch (Exception ignored) { // GCM API could crash if it's disabled, ignore crashes at this point and continue } } } }
JobApi api = JobApi.getDefault(context); if (api == JobApi.V_14 && !api.isSupported(context)) { throw new JobManagerCreateException("All APIs are disabled, cannot schedule any job");