1 /* 2 * Copyright 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package androidx.work.impl.background.firebase; 18 19 import android.app.AlarmManager; 20 import android.app.PendingIntent; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.os.Build; 24 import android.support.annotation.NonNull; 25 import android.support.annotation.RestrictTo; 26 import android.util.Log; 27 28 import androidx.work.impl.Scheduler; 29 import androidx.work.impl.model.WorkSpec; 30 import androidx.work.impl.utils.IdGenerator; 31 32 import com.firebase.jobdispatcher.FirebaseJobDispatcher; 33 import com.firebase.jobdispatcher.GooglePlayDriver; 34 import com.firebase.jobdispatcher.Job; 35 import com.google.android.gms.common.ConnectionResult; 36 import com.google.android.gms.common.GoogleApiAvailability; 37 38 /** 39 * A class that schedules work using {@link FirebaseJobDispatcher}. 40 * 41 * @hide 42 */ 43 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) 44 public class FirebaseJobScheduler implements Scheduler { 45 46 private static final String TAG = "FirebaseJobScheduler"; 47 48 private final Context mAppContext; 49 private final FirebaseJobDispatcher mDispatcher; 50 private final FirebaseJobConverter mJobConverter; 51 52 private IdGenerator mIdGenerator; 53 private AlarmManager mAlarmManager; 54 55 public FirebaseJobScheduler(@NonNull Context context) { 56 mAppContext = context.getApplicationContext(); 57 boolean isPlayServicesAvailable = GoogleApiAvailability.getInstance() 58 .isGooglePlayServicesAvailable(mAppContext) == ConnectionResult.SUCCESS; 59 if (!isPlayServicesAvailable) { 60 throw new IllegalStateException("Google Play Services not available"); 61 } 62 mDispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(mAppContext)); 63 mJobConverter = new FirebaseJobConverter(mDispatcher); 64 } 65 66 @Override 67 public void schedule(WorkSpec... workSpecs) { 68 for (WorkSpec workSpec : workSpecs) { 69 if (workSpec.calculateNextRunTime() > System.currentTimeMillis()) { 70 scheduleLater(workSpec); 71 } else { 72 scheduleNow(workSpec); 73 } 74 } 75 } 76 77 @Override 78 public void cancel(@NonNull String workSpecId) { 79 mDispatcher.cancel(workSpecId); 80 } 81 82 void scheduleNow(WorkSpec workSpec) { 83 Job job = mJobConverter.convert(workSpec); 84 Log.d(TAG, String.format("Scheduling work now, ID: %s", workSpec.id)); 85 int result = mDispatcher.schedule(job); 86 if (result != FirebaseJobDispatcher.SCHEDULE_RESULT_SUCCESS) { 87 Log.e(TAG, String.format("Schedule failed. Result = %s", result)); 88 } 89 } 90 91 private void scheduleLater(WorkSpec workSpec) { 92 if (mAlarmManager == null) { 93 mAlarmManager = (AlarmManager) mAppContext.getSystemService(Context.ALARM_SERVICE); 94 } 95 if (mIdGenerator == null) { 96 mIdGenerator = new IdGenerator(mAppContext); 97 } 98 Log.d(TAG, String.format("Scheduling work later, ID: %s", workSpec.id)); 99 PendingIntent pendingIntent = createScheduleLaterPendingIntent(workSpec); 100 101 // This wakes up the device at exactly the next run time. 102 // A wakeup is necessary because the device could be sleeping when this alarm is fired. 103 long triggerAtMillis = workSpec.calculateNextRunTime(); 104 if (Build.VERSION.SDK_INT >= 19) { 105 mAlarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent); 106 } else { 107 mAlarmManager.set(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent); 108 } 109 } 110 111 private PendingIntent createScheduleLaterPendingIntent(WorkSpec workSpec) { 112 Intent intent = new Intent(mAppContext, FirebaseDelayedJobAlarmReceiver.class); 113 intent.putExtra(FirebaseDelayedJobAlarmReceiver.WORKSPEC_ID_KEY, workSpec.id); 114 int requestCode = mIdGenerator.nextFirebaseAlarmId(); 115 return PendingIntent.getBroadcast(mAppContext, requestCode, intent, 0); 116 } 117 } 118