diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index d97f75ee1..914e7a868 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ diff --git a/app/build.gradle b/app/build.gradle index 9dceffa81..3db21e1fd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -153,6 +153,8 @@ dependencies { implementation('com.afollestad.material-dialogs:commons:0.9.2.3', { exclude group: 'com.android.support' }) + // Android job + implementation 'com.evernote:android-job:1.2.6' debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1' releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1' // Optional, if you use support library fragments: diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 816de6266..447695b04 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -300,13 +300,6 @@ - - - - - - - \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/App.kt b/app/src/main/java/org/autojs/autojs/App.kt index ff06a8d71..2dde69f34 100644 --- a/app/src/main/java/org/autojs/autojs/App.kt +++ b/app/src/main/java/org/autojs/autojs/App.kt @@ -13,6 +13,7 @@ import androidx.localbroadcastmanager.content.LocalBroadcastManager import com.bumptech.glide.Glide import com.bumptech.glide.request.target.SimpleTarget import com.bumptech.glide.request.transition.Transition +import com.evernote.android.job.JobRequest import com.flurry.android.FlurryAgent import com.squareup.leakcanary.LeakCanary import com.stardust.app.GlobalAppContext @@ -84,7 +85,7 @@ class App : MultiDexApplication() { GlobalKeyObserver.init() } setupDrawableImageLoader() - TimedTaskScheduler.checkTasksRepeatedlyIfNeeded(this) + TimedTaskScheduler.init(this) initDynamicBroadcastReceivers() } diff --git a/app/src/main/java/org/autojs/autojs/storage/database/TimedTaskDatabase.java b/app/src/main/java/org/autojs/autojs/storage/database/TimedTaskDatabase.java index 868de3df2..b6f343e5a 100644 --- a/app/src/main/java/org/autojs/autojs/storage/database/TimedTaskDatabase.java +++ b/app/src/main/java/org/autojs/autojs/storage/database/TimedTaskDatabase.java @@ -21,6 +21,7 @@ public TimedTaskDatabase(Context context) { @Override protected ContentValues asContentValues(TimedTask model) { ContentValues values = new ContentValues(); + values.put("id", model.getId()); values.put("time", model.getTimeFlag()); values.put("scheduled", model.isScheduled()); values.put("delay", model.getDelay()); @@ -34,7 +35,7 @@ protected ContentValues asContentValues(TimedTask model) { @Override protected TimedTask createModelFromCursor(Cursor cursor) { TimedTask task = new TimedTask(); - task.setId(cursor.getInt(0)); + task.setId(cursor.getLong(0)); task.setTimeFlag(cursor.getLong(1)); task.setScheduled(cursor.getInt(2) != 0); task.setDelay(cursor.getLong(3)); diff --git a/app/src/main/java/org/autojs/autojs/timing/TimedTaskManager.java b/app/src/main/java/org/autojs/autojs/timing/TimedTaskManager.java index 5f1dada07..4ad701ac5 100644 --- a/app/src/main/java/org/autojs/autojs/timing/TimedTaskManager.java +++ b/app/src/main/java/org/autojs/autojs/timing/TimedTaskManager.java @@ -17,6 +17,7 @@ import io.reactivex.Flowable; import io.reactivex.Observable; +import io.reactivex.functions.Consumer; /** * Created by Stardust on 2017/11/27. @@ -41,14 +42,6 @@ public TimedTaskManager(Context context) { mContext = context; mTimedTaskDatabase = new TimedTaskDatabase(context); mIntentTaskDatabase = new IntentTaskDatabase(context); - mTimedTaskDatabase.getModelChange().subscribe(change -> { - int action = change.getAction(); - if (action == ModelChange.DELETE && countTasks() == 0) { - TimedTaskScheduler.stopRtcRepeating(mContext); - } else if (action == ModelChange.INSERT) { - TimedTaskScheduler.checkTasksRepeatedlyIfNeeded(mContext); - } - }); } @SuppressLint("CheckResult") @@ -76,15 +69,17 @@ public void removeTask(TimedTask timedTask) { @SuppressLint("CheckResult") public void addTask(TimedTask timedTask) { mTimedTaskDatabase.insert(timedTask) - .subscribe(Observers.emptyConsumer(), Throwable::printStackTrace);; - TimedTaskScheduler.scheduleTaskIfNeeded(mContext, timedTask); + .subscribe(id -> { + timedTask.setId(id); + TimedTaskScheduler.scheduleTaskIfNeeded(mContext, timedTask); + }, Throwable::printStackTrace); } @SuppressLint("CheckResult") public void addTask(IntentTask intentTask) { mIntentTaskDatabase.insert(intentTask) .subscribe(i -> { - if(!TextUtils.isEmpty(intentTask.getAction())){ + if (!TextUtils.isEmpty(intentTask.getAction())) { App.Companion.getApp().getDynamicBroadcastReceivers() .register(intentTask); } @@ -95,7 +90,7 @@ public void addTask(IntentTask intentTask) { public void removeTask(IntentTask intentTask) { mIntentTaskDatabase.delete(intentTask) .subscribe(i -> { - if(!TextUtils.isEmpty(intentTask.getAction())){ + if (!TextUtils.isEmpty(intentTask.getAction())) { App.Companion.getApp().getDynamicBroadcastReceivers() .unregister(intentTask.getAction()); } @@ -139,11 +134,17 @@ public void updateTask(TimedTask task) { TimedTaskScheduler.scheduleTaskIfNeeded(mContext, task); } + @SuppressLint("CheckResult") + public void updateTaskWithoutReScheduling(TimedTask task) { + mTimedTaskDatabase.update(task) + .subscribe(Observers.emptyConsumer(), Throwable::printStackTrace); + } + @SuppressLint("CheckResult") public void updateTask(IntentTask task) { mIntentTaskDatabase.update(task) .subscribe(i -> { - if(i > 0 && !TextUtils.isEmpty(task.getAction())){ + if (i > 0 && !TextUtils.isEmpty(task.getAction())) { App.Companion.getApp().getDynamicBroadcastReceivers() .register(task); } diff --git a/app/src/main/java/org/autojs/autojs/timing/TimedTaskScheduler.java b/app/src/main/java/org/autojs/autojs/timing/TimedTaskScheduler.java index 623566663..159108923 100644 --- a/app/src/main/java/org/autojs/autojs/timing/TimedTaskScheduler.java +++ b/app/src/main/java/org/autojs/autojs/timing/TimedTaskScheduler.java @@ -3,6 +3,8 @@ import android.annotation.SuppressLint; import android.app.AlarmManager; import android.app.PendingIntent; +import android.app.job.JobInfo; +import android.app.job.JobScheduler; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -10,8 +12,22 @@ import android.provider.AlarmClock; import android.util.Log; +import com.evernote.android.job.Job; +import com.evernote.android.job.JobCreator; +import com.evernote.android.job.JobManager; +import com.evernote.android.job.JobRequest; +import com.evernote.android.job.util.support.PersistableBundleCompat; + +import org.autojs.autojs.App; +import org.autojs.autojs.external.ScriptIntents; +import org.autojs.autojs.storage.database.TimedTaskDatabase; +import org.jetbrains.annotations.NotNull; + import java.util.concurrent.TimeUnit; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; @@ -20,24 +36,17 @@ * Created by Stardust on 2017/11/27. */ -public class TimedTaskScheduler extends BroadcastReceiver { +public class TimedTaskScheduler { - public static final String ACTION_CHECK_TASK = "com.stardust.autojs.action.check_task"; private static final String LOG_TAG = "TimedTaskScheduler"; - private static final int REQUEST_CODE_CHECK_TASK_REPEATEDLY = 4000; - private static final long INTERVAL = TimeUnit.MINUTES.toMillis(1); private static final long ONE_HOUR = TimeUnit.HOURS.toMillis(1); - private static PendingIntent sCheckTasksPendingIntent; - @Override - public void onReceive(Context context, Intent intent) { - Log.d(LOG_TAG, "onReceiveRtcWakeup"); - checkTasks(context); - setupNextRtcWakeup(context, System.currentTimeMillis() + INTERVAL); - } + private static final String JOB_TAG_CHECK_TASKS = "checkTasks"; + @SuppressLint("CheckResult") public static void checkTasks(Context context) { + Log.d(LOG_TAG, "check tasks"); TimedTaskManager.getInstance().getAllTasks() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -52,73 +61,89 @@ public static void scheduleTaskIfNeeded(Context context, TimedTask timedTask) { scheduleTask(context, timedTask, millis); TimedTaskManager.getInstance() .notifyTaskScheduled(timedTask); - } - - private static void scheduleTask(Context context, TimedTask timedTask, long millis) { - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - if (millis <= System.currentTimeMillis()) { - context.sendBroadcast(timedTask.createIntent()); + private synchronized static void scheduleTask(Context context, TimedTask timedTask, long millis) { + if (timedTask.isScheduled()) { return; } - assert alarmManager != null; - // FIXME: 2017/11/28 requestCode may > 65535 - PendingIntent op = timedTask.createPendingIntent(context); - setExactCompat(alarmManager, op, millis); - } - - private static void setExactCompat(AlarmManager alarmManager, PendingIntent op, long millis) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, millis, op); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - alarmManager.setAlarmClock(new AlarmManager.AlarmClockInfo(millis, null), op); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - alarmManager.setExact(AlarmManager.RTC_WAKEUP, millis, op); - } else { - alarmManager.set(AlarmManager.RTC_WAKEUP, millis, op); + long timeWindow = millis - System.currentTimeMillis(); + Log.d(LOG_TAG, "schedule task: task = " + timedTask + ", millis = " + millis + ", timeWindow = " + timeWindow); + timedTask.setScheduled(true); + TimedTaskManager.getInstance().updateTaskWithoutReScheduling(timedTask); + if (timeWindow <= 0) { + runTask(context, timedTask); + return; } + new JobRequest.Builder(String.valueOf(timedTask.getId())) + .setExact(timeWindow) + .build() + .schedule(); } + public static void cancel(Context context, TimedTask timedTask) { + Log.d(LOG_TAG, "cancel task: task = " + timedTask); + JobManager.instance().cancelAllForTag(String.valueOf(timedTask.getId())); + } - public static void checkTasksRepeatedlyIfNeeded(Context context) { - if (TimedTaskManager.getInstance().countTasks() > 0) { - setupNextRtcWakeup(context, System.currentTimeMillis() + 5000); - } + public static void init(@NotNull Context context) { + JobManager.create(context).addJobCreator(tag -> { + if (tag.equals(JOB_TAG_CHECK_TASKS)) { + return new CheckTasksJob(context); + } else { + return new TimedTaskJob(context); + } + }); + new JobRequest.Builder(JOB_TAG_CHECK_TASKS) + .setPeriodic(TimeUnit.MINUTES.toMillis(20)) + .build() + .scheduleAsync(); + checkTasks(context); } - private static void setupNextRtcWakeup(Context context, long millis) { - Log.v(LOG_TAG, "setupNextRtcWakeup: at " + millis); - if (millis <= 0) { - throw new IllegalArgumentException("millis <= 0: " + millis); - } - AlarmManager alarmManager = getAlarmManager(context); - setExactCompat(alarmManager, createTaskCheckPendingIntent(context), millis); + private static void runTask(Context context, TimedTask task) { + Log.d(LOG_TAG, "run task: task = " + task); + Intent intent = task.createIntent(); + ScriptIntents.handleIntent(context, intent); + TimedTaskManager.getInstance().notifyTaskFinished(task.getId()); } + private static class TimedTaskJob extends Job { - public static void cancel(Context context, TimedTask timedTask) { - AlarmManager alarmManager = getAlarmManager(context); - alarmManager.cancel(timedTask.createPendingIntent(context)); - } + private final Context mContext; - public static void stopRtcRepeating(Context context) { - Log.v(LOG_TAG, "stopRtcRepeating"); - AlarmManager alarmManager = getAlarmManager(context); - alarmManager.cancel(createTaskCheckPendingIntent(context)); - } + TimedTaskJob(Context context) { + mContext = context; + } - private static AlarmManager getAlarmManager(Context context) { - AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - assert alarmManager != null; - return alarmManager; + @NonNull + @Override + protected Result onRunJob(@NonNull Params params) { + long id = Long.parseLong(params.getTag()); + TimedTask task = TimedTaskManager.getInstance().getTimedTask(id); + Log.d(LOG_TAG, "onRunJob: id = " + id + ", task = " + task); + if (task == null) { + return Result.FAILURE; + } + runTask(mContext, task); + return Result.SUCCESS; + } } - private static PendingIntent createTaskCheckPendingIntent(Context context) { - if (sCheckTasksPendingIntent == null) { - sCheckTasksPendingIntent = PendingIntent.getBroadcast(context, REQUEST_CODE_CHECK_TASK_REPEATEDLY, - new Intent(TimedTaskScheduler.ACTION_CHECK_TASK), PendingIntent.FLAG_UPDATE_CURRENT); + private static class CheckTasksJob extends Job { + private final Context mContext; + + CheckTasksJob(Context context) { + mContext = context; + } + + @NonNull + @Override + protected Result onRunJob(@NonNull Params params) { + checkTasks(mContext); + return Result.SUCCESS; } - return sCheckTasksPendingIntent; } + + } diff --git a/common/release/output.json b/common/release/output.json index cf367a7b5..f27a16b10 100644 --- a/common/release/output.json +++ b/common/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":453,"versionName":"4.1.0 Alpha3","enabled":true,"outputFile":"commonRelease-4.1.0 Alpha3.apk","fullName":"commonRelease","baseName":"common-release"},"path":"commonRelease-4.1.0 Alpha3.apk","properties":{}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":454,"versionName":"4.1.0 Alpha4","enabled":true,"outputFile":"commonRelease-4.1.0 Alpha4.apk","fullName":"commonRelease","baseName":"common-release"},"path":"commonRelease-4.1.0 Alpha4.apk","properties":{}}] \ No newline at end of file diff --git a/project-versions.json b/project-versions.json index 9ff25112c..fc5c0f726 100644 --- a/project-versions.json +++ b/project-versions.json @@ -1,6 +1,6 @@ { - "appVersionCode": 453, - "appVersionName": "4.1.0 Alpha3", + "appVersionCode": 454, + "appVersionName": "4.1.0 Alpha4", "target": 28, "mini": 17, "compile": 28,