From 025e594a010e702ac6ef6a51d6450c999808e003 Mon Sep 17 00:00:00 2001 From: hyb1996 <946994919@qq.com> Date: Fri, 28 Sep 2018 00:56:34 +0800 Subject: [PATCH] feat: intent task --- .idea/caches/build_file_checksums.ser | Bin 733 -> 733 bytes .idea/dictionaries/Stardust.xml | 1 + app/build.gradle | 5 - app/src/main/AndroidManifest.xml | 28 ++- app/src/main/java/org/autojs/autojs/App.java | 26 +-- .../BaseBroadcastReceiver.java} | 18 +- .../receiver/DynamicBroadcastReceiver.java | 79 +++++++ .../receiver/StaticBroadcastReceiver.java | 40 ++++ .../tasker/TaskerScriptEditActivity.java | 5 +- .../autojs/storage/database/BaseModel.java | 27 +++ .../autojs/storage/database/Database.java | 173 +++++++++++++++ .../storage/database/IntentTaskDatabase.java | 63 +++++- .../autojs/storage/database/ModelChange.java | 14 +- .../storage/database/TimedTaskDatabase.java | 74 ++++++- .../org/autojs/autojs/timing/IntentTask.java | 36 +-- .../autojs/autojs/timing/TaskReceiver.java | 2 +- .../org/autojs/autojs/timing/TimedTask.java | 55 +---- .../autojs/timing/TimedTaskManager.java | 114 +++++----- .../autojs/timing/TimedTaskScheduler.java | 6 +- .../autojs/ui/common/ScriptOperations.java | 2 + .../filechooser/FileChooserDialogBuilder.java | 2 +- .../org/autojs/autojs/ui/main/task/Task.java | 44 +++- .../autojs/autojs/ui/main/task/TaskGroup.java | 39 +++- .../ui/main/task/TaskListRecyclerView.java | 35 ++- .../ui/timing/TimedTaskSettingActivity.java | 56 +++-- .../layout/activity_timed_task_setting.xml | 78 ++++++- app/src/main/res/values/strings.xml | 8 +- .../main/java/com/stardust/util/BiMap.java | 11 + .../main/java/com/stardust/util/BiMaps.java | 206 ++++++++++++++++++ 29 files changed, 1008 insertions(+), 239 deletions(-) rename app/src/main/java/org/autojs/autojs/external/{boot/BootCompleteReceiver.java => receiver/BaseBroadcastReceiver.java} (71%) create mode 100644 app/src/main/java/org/autojs/autojs/external/receiver/DynamicBroadcastReceiver.java create mode 100644 app/src/main/java/org/autojs/autojs/external/receiver/StaticBroadcastReceiver.java create mode 100644 app/src/main/java/org/autojs/autojs/storage/database/BaseModel.java create mode 100644 app/src/main/java/org/autojs/autojs/storage/database/Database.java create mode 100644 common/src/main/java/com/stardust/util/BiMap.java create mode 100644 common/src/main/java/com/stardust/util/BiMaps.java diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 4c2daf666ae5e458ff1e8b893dcae8e74665c0b2..d6a560997893d2dd4954d965d7a17989a86d8ae8 100644 GIT binary patch delta 37 vcmV+=0NVfE1>FUZmjz}S%&#PooOck8jREz+vWJovg&cHd`kmObBLTVrK?xA^ delta 37 vcmV+=0NVfE1>FUZmjz|;4iq1eoOcjU##(t~{6~5o(wuJUQQjuABLTVrFk}!9 diff --git a/.idea/dictionaries/Stardust.xml b/.idea/dictionaries/Stardust.xml index 4218d2eb..30ec0d12 100644 --- a/.idea/dictionaries/Stardust.xml +++ b/.idea/dictionaries/Stardust.xml @@ -3,6 +3,7 @@ capturer dismissable + flowable interruptible loopers prefill diff --git a/app/build.gradle b/app/build.gradle index 7c4f2275..0b280778 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -129,11 +129,6 @@ dependencies { exclude group: 'com.android.support' }) annotationProcessor 'com.github.bumptech.glide:compiler:4.2.0' - //dbflow - annotationProcessor "com.github.Raizlabs.DBFlow:dbflow-processor:4.1.2" - compile "com.github.Raizlabs.DBFlow:dbflow-core:4.1.2" - compile "com.github.Raizlabs.DBFlow:dbflow:4.1.2" - compile "com.github.Raizlabs.DBFlow:dbflow-rx2:4.1.2" //joda time compile 'joda-time:joda-time:2.9.9' // Tasker Plugin diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c0c87487..aa1cb820 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -133,10 +133,36 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/org/autojs/autojs/App.java b/app/src/main/java/org/autojs/autojs/App.java index b6335a4d..e42fae4d 100644 --- a/app/src/main/java/org/autojs/autojs/App.java +++ b/app/src/main/java/org/autojs/autojs/App.java @@ -1,11 +1,9 @@ package org.autojs.autojs; -import android.app.Application; import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; -import android.support.multidex.MultiDex; import android.support.multidex.MultiDexApplication; import android.view.View; import android.widget.ImageView; @@ -13,17 +11,17 @@ import android.widget.ImageView; import com.bumptech.glide.request.target.SimpleTarget; import com.bumptech.glide.request.transition.Transition; import com.flurry.android.FlurryAgent; -import com.raizlabs.android.dbflow.config.DatabaseConfig; -import com.raizlabs.android.dbflow.config.FlowConfig; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.runtime.DirectModelNotifier; import com.squareup.leakcanary.LeakCanary; import com.stardust.app.GlobalAppContext; import com.stardust.autojs.core.ui.inflater.ImageLoader; import com.stardust.autojs.core.ui.inflater.util.Drawables; +import com.stardust.theme.ThemeColor; +import com.stardust.theme.ThemeColorManager; +import com.tencent.bugly.crashreport.CrashReport; import org.autojs.autojs.autojs.AutoJs; import org.autojs.autojs.autojs.key.GlobalKeyObserver; +import org.autojs.autojs.external.receiver.DynamicBroadcastReceiver; import org.autojs.autojs.network.GlideApp; import org.autojs.autojs.storage.database.IntentTaskDatabase; import org.autojs.autojs.storage.database.TimedTaskDatabase; @@ -31,11 +29,6 @@ import org.autojs.autojs.timing.TimedTaskScheduler; import org.autojs.autojs.tool.CrashHandler; import org.autojs.autojs.ui.error.ErrorReportActivity; -import com.stardust.theme.ThemeColor; -import com.stardust.theme.ThemeColorManager; -import com.tencent.bugly.crashreport.CrashReport; - - import java.lang.ref.WeakReference; /** @@ -48,6 +41,7 @@ public class App extends MultiDexApplication { private static final String BUGLY_APP_ID = "19b3607b53"; private static WeakReference instance; + private DynamicBroadcastReceiver mDynamicBroadcastReceiver; public static App getApp() { return instance.get(); @@ -92,17 +86,10 @@ public class App extends MultiDexApplication { return; } //LeakCanary.install(this); + } private void init() { - FlowManager.init(FlowConfig.builder(this) - .addDatabaseConfig(DatabaseConfig.builder(TimedTaskDatabase.class) - .modelNotifier(DirectModelNotifier.get()) - .build()) - .addDatabaseConfig(DatabaseConfig.builder(IntentTaskDatabase.class) - .modelNotifier(DirectModelNotifier.get()) - .build()) - .build()); ThemeColorManager.setDefaultThemeColor(new ThemeColor(getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorPrimaryDark), getResources().getColor(R.color.colorAccent))); ThemeColorManager.init(this); AutoJs.initInstance(this); @@ -111,6 +98,7 @@ public class App extends MultiDexApplication { } setupDrawableImageLoader(); TimedTaskScheduler.checkTasksRepeatedlyIfNeeded(this); + mDynamicBroadcastReceiver = new DynamicBroadcastReceiver(this); } private void setupDrawableImageLoader() { diff --git a/app/src/main/java/org/autojs/autojs/external/boot/BootCompleteReceiver.java b/app/src/main/java/org/autojs/autojs/external/receiver/BaseBroadcastReceiver.java similarity index 71% rename from app/src/main/java/org/autojs/autojs/external/boot/BootCompleteReceiver.java rename to app/src/main/java/org/autojs/autojs/external/receiver/BaseBroadcastReceiver.java index 4e6be901..a7d68b87 100644 --- a/app/src/main/java/org/autojs/autojs/external/boot/BootCompleteReceiver.java +++ b/app/src/main/java/org/autojs/autojs/external/receiver/BaseBroadcastReceiver.java @@ -1,4 +1,4 @@ -package org.autojs.autojs.external.boot; +package org.autojs.autojs.external.receiver; import android.annotation.SuppressLint; import android.content.BroadcastReceiver; @@ -7,6 +7,7 @@ import android.content.Intent; import android.util.Log; import android.widget.Toast; +import com.stardust.app.GlobalAppContext; import com.stardust.autojs.execution.ExecutionConfig; import org.autojs.autojs.autojs.AutoJs; @@ -17,23 +18,25 @@ import org.autojs.autojs.timing.TimedTaskManager; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; -public class BootCompleteReceiver extends BroadcastReceiver { +public class BaseBroadcastReceiver extends BroadcastReceiver { - private static final String LOG_TAG = "BootCompleteReceiver"; + private static final String LOG_TAG = "BaseBroadcastReceiver"; @SuppressLint("CheckResult") - @Override public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { - Log.i(LOG_TAG, "on boot complete"); + Log.d(LOG_TAG, "onReceive: action = " + intent.getAction() + ", intent = " + intent); + try { TimedTaskManager.getInstance().getIntentTaskOfAction(intent.getAction()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(intentTask -> runTask(context, intent, intentTask), Throwable::printStackTrace); + } catch (Exception e) { + GlobalAppContext.toast(e.getMessage()); } } - private void runTask(Context context, Intent intent, IntentTask task) { + static void runTask(Context context, Intent intent, IntentTask task) { + Log.d(LOG_TAG, "runTask: action = " + intent.getAction() + ", script = " + task.getScriptPath()); ScriptFile file = new ScriptFile(task.getScriptPath()); ExecutionConfig config = new ExecutionConfig(); config.setArgument("intent", intent.clone()); @@ -45,4 +48,5 @@ public class BootCompleteReceiver extends BroadcastReceiver { Toast.makeText(context, e.getMessage(), Toast.LENGTH_LONG).show(); } } + } diff --git a/app/src/main/java/org/autojs/autojs/external/receiver/DynamicBroadcastReceiver.java b/app/src/main/java/org/autojs/autojs/external/receiver/DynamicBroadcastReceiver.java new file mode 100644 index 00000000..62ff15c5 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/external/receiver/DynamicBroadcastReceiver.java @@ -0,0 +1,79 @@ +package org.autojs.autojs.external.receiver; + +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.media.AudioManager; +import android.os.Build; + +import org.autojs.autojs.BuildConfig; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import static android.content.Intent.ACTION_BATTERY_CHANGED; +import static android.content.Intent.ACTION_CONFIGURATION_CHANGED; +import static android.content.Intent.ACTION_DATE_CHANGED; +import static android.content.Intent.ACTION_PACKAGES_SUSPENDED; +import static android.content.Intent.ACTION_PACKAGES_UNSUSPENDED; +import static android.content.Intent.ACTION_PACKAGE_ADDED; +import static android.content.Intent.ACTION_PACKAGE_CHANGED; +import static android.content.Intent.ACTION_PACKAGE_DATA_CLEARED; +import static android.content.Intent.ACTION_PACKAGE_REMOVED; +import static android.content.Intent.ACTION_PACKAGE_RESTARTED; +import static android.content.Intent.ACTION_POWER_CONNECTED; +import static android.content.Intent.ACTION_POWER_DISCONNECTED; +import static android.content.Intent.ACTION_SHUTDOWN; +import static android.content.Intent.ACTION_TIMEZONE_CHANGED; +import static android.content.Intent.ACTION_TIME_CHANGED; +import static android.content.Intent.ACTION_TIME_TICK; +import static android.content.Intent.ACTION_UID_REMOVED; + +public class DynamicBroadcastReceiver extends BaseBroadcastReceiver { + + private static final List DEFAULT_ACTIONS = new ArrayList<>(Arrays.asList( + ACTION_TIME_TICK, + ACTION_BATTERY_CHANGED, + ACTION_CONFIGURATION_CHANGED + )); + + static { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + DEFAULT_ACTIONS.addAll(Arrays.asList( + ACTION_PACKAGES_SUSPENDED, + ACTION_PACKAGES_UNSUSPENDED + )); + } + } + + private boolean mRegistered = false; + private final Set mActions = new LinkedHashSet<>(); + private final Context mContext; + + + public DynamicBroadcastReceiver(Context context) { + mContext = context; + register(DEFAULT_ACTIONS); + } + + public void register(List actions) { + int oldSize = mActions.size(); + mActions.addAll(actions); + if (oldSize == mActions.size()) { + return; + } + IntentFilter filter = new IntentFilter(); + for (String action : mActions) { + filter.addAction(action); + } + if(mRegistered){ + mContext.unregisterReceiver(this); + } + mContext.registerReceiver(this, filter); + mRegistered = true; + } + +} diff --git a/app/src/main/java/org/autojs/autojs/external/receiver/StaticBroadcastReceiver.java b/app/src/main/java/org/autojs/autojs/external/receiver/StaticBroadcastReceiver.java new file mode 100644 index 00000000..55b93726 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/external/receiver/StaticBroadcastReceiver.java @@ -0,0 +1,40 @@ +package org.autojs.autojs.external.receiver; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class StaticBroadcastReceiver extends BaseBroadcastReceiver { + + static final List ACTIONS = new ArrayList<>(Arrays.asList( + "android.intent.action.BOOT_COMPLETED", + "android.intent.action.QUICKBOOT_POWERON", + "android.intent.action.TIME_SET", + "android.intent.action.TIMEZONE_CHANGED", + "android.intent.action.PACKAGE_ADDED", + "android.intent.action.PACKAGE_CHANGED", + "android.intent.action.PACKAGE_DATA_CLEARED", + "android.intent.action.PACKAGE_REMOVED", + "android.intent.action.PACKAGE_RESTARTED", + "android.intent.action.UID_REMOVED", + "android.intent.action.ACTION_POWER_CONNECTED", + "android.intent.action.ACTION_POWER_DISCONNECTED", + "android.intent.action.ACTION_SHUTDOWN", + "android.intent.action.DATE_CHANGED", + "android.intent.action.DREAMING_STARTED", + "android.intent.action.DREAMING_STOPPED", + "android.intent.action.HEADSET_PLUG", + "android.intent.action.INPUT_METHOD_CHANGED", + "android.intent.action.LOCALE_CHANGED", + "android.intent.action.MEDIA_BUTTON", + "android.intent.action.MEDIA_CHECKING", + "android.intent.action.MEDIA_MOUNTED", + "android.intent.action.PACKAGE_FIRST_LAUNCH", + "android.intent.action.PROVIDER_CHANGED", + "android.intent.action.WALLPAPER_CHANGED", + "android.intent.action.USER_UNLOCKED", + "android.intent.action.USER_PRESENT", + "android.net.conn.CONNECTIVITY_CHANGE" + )); + +} diff --git a/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java b/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java index 4c0de0a1..764ad4e5 100644 --- a/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java +++ b/app/src/main/java/org/autojs/autojs/external/tasker/TaskerScriptEditActivity.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.widget.Toast; import org.autojs.autojs.R; +import org.autojs.autojs.timing.TaskReceiver; import org.autojs.autojs.tool.EmptyObservers; import org.autojs.autojs.ui.BaseActivity; import org.autojs.autojs.ui.edit.EditorView; @@ -29,7 +30,9 @@ import static org.autojs.autojs.ui.edit.EditorView.EXTRA_SAVE_ENABLED; @EActivity(R.layout.activity_tasker_script_edit) public class TaskerScriptEditActivity extends BaseActivity { - public static final int REQUEST_CODE = "Love you. Can we go back?".hashCode() >> 16; + public static final int REQUEST_CODE = 10016; + public static final String EXTRA_INTENT_TASK_ID = "intent_task_id"; + public static final String EXTRA_TASK_ID = TaskReceiver.EXTRA_TASK_ID; public static void edit(Activity activity, String title, String summary, String content) { activity.startActivityForResult(new Intent(activity, TaskerScriptEditActivity_.class) diff --git a/app/src/main/java/org/autojs/autojs/storage/database/BaseModel.java b/app/src/main/java/org/autojs/autojs/storage/database/BaseModel.java new file mode 100644 index 00000000..76be2ef1 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/storage/database/BaseModel.java @@ -0,0 +1,27 @@ +package org.autojs.autojs.storage.database; + + +public abstract class BaseModel { + private long mId; + + public void setId(long id) { + mId = id; + } + + public long getId() { + return mId; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BaseModel baseModel = (BaseModel) o; + return mId == baseModel.mId; + } + + @Override + public int hashCode() { + return (int)(mId ^ (mId >>> 32)); + } +} diff --git a/app/src/main/java/org/autojs/autojs/storage/database/Database.java b/app/src/main/java/org/autojs/autojs/storage/database/Database.java new file mode 100644 index 00000000..d42fea78 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/storage/database/Database.java @@ -0,0 +1,173 @@ +package org.autojs.autojs.storage.database; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +import org.autojs.autojs.timing.IntentTask; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.Callable; + +import io.reactivex.Flowable; +import io.reactivex.Observable; +import io.reactivex.schedulers.Schedulers; +import io.reactivex.subjects.PublishSubject; + +public abstract class Database { + + private final SQLiteDatabase mWritableSQLiteDatabase; + private final SQLiteDatabase mReadableSQLiteDatabase; + private final String mTable; + private final PublishSubject> mModelChange = PublishSubject.create(); + + public Database(SQLiteOpenHelper sqLiteOpenHelper, String table) { + mWritableSQLiteDatabase = sqLiteOpenHelper.getWritableDatabase(); + mReadableSQLiteDatabase = sqLiteOpenHelper.getWritableDatabase(); + mTable = table; + } + + public Observable exec(Callable callable) { + return Observable.fromCallable(callable) + .subscribeOn(Schedulers.io()); + } + + public Flowable execFlowable(Callable callable) { + return Flowable.fromCallable(callable) + .subscribeOn(Schedulers.io()); + } + + public PublishSubject> getModelChange() { + return mModelChange; + } + + public Observable delete(M model) { + return exec(() -> { + int delete = mWritableSQLiteDatabase.delete(mTable, "id = ?", + new String[]{String.valueOf(model.getId())}); + if (delete >= 1) { + mModelChange.onNext(new ModelChange<>(model, ModelChange.DELETE)); + } + return delete; + }); + } + + public Observable update(M model) { + return exec(() -> { + ContentValues values = asContentValues(model); + int update = mWritableSQLiteDatabase.update(mTable, values, "id = ?", arg(model.getId())); + if (update >= 1) { + mModelChange.onNext(new ModelChange<>(model, ModelChange.UPDATE)); + } + return update; + }); + } + + + public Observable insert(M model) { + return exec(() -> { + ContentValues values = asContentValues(model); + long id = mWritableSQLiteDatabase.insertOrThrow(mTable, null, values); + if (id >= 0) { + model.setId(id); + mModelChange.onNext(new ModelChange<>(model, ModelChange.INSERT)); + } + return id; + }); + } + + + protected abstract M createModelFromCursor(Cursor cursor); + + protected abstract ContentValues asContentValues(M model); + + public M queryById(long id) { + Cursor cursor = mReadableSQLiteDatabase.rawQuery("SELECT * FROM " + mTable + " WHERE id = ?", arg(id)); + if (!cursor.moveToFirst()) { + return null; + } + M model = createModelFromCursor(cursor); + cursor.close(); + return model; + } + + + public Flowable queryAllAsFlowable() { + return execFlowable(() -> + mReadableSQLiteDatabase.rawQuery("SELECT * FROM " + mTable, null) + ) + .flatMap(cursor -> Flowable.fromIterable(() -> new CursorIterator(cursor))) + .map(this::createModelFromCursor); + } + + public List queryAll() { + ArrayList list = new ArrayList<>(); + Cursor cursor = mReadableSQLiteDatabase.rawQuery("SELECT * FROM " + mTable, null); + while (cursor.moveToNext()) { + list.add(createModelFromCursor(cursor)); + } + cursor.close(); + return list; + } + + public long count() { + Cursor cursor = mReadableSQLiteDatabase.rawQuery("SELECT COUNT(*) FROM " + mTable, null); + if (cursor.moveToFirst()) { + return cursor.getLong(0); + } + cursor.close(); + return 0; + } + + + public Flowable query(String sql, Object... args) { + String[] strArgs = args(args); + return execFlowable(() -> + mReadableSQLiteDatabase.query(mTable, null, sql, strArgs, null, null, null) + ) + .flatMap(cursor -> Flowable.fromIterable(() -> new CursorIterator(cursor))) + .map(this::createModelFromCursor); + } + + private String[] args(Object[] args) { + if (args == null || args.length == 0) { + return null; + } + String[] a = new String[args.length]; + for (int i = 0; i < args.length; i++) { + a[i] = String.valueOf(args[i]); + } + return a; + } + + private String[] arg(Object value) { + return new String[]{String.valueOf(value)}; + } + + + private static class CursorIterator implements Iterator { + + private final Cursor mCursor; + + private CursorIterator(Cursor cursor) { + mCursor = cursor; + } + + @Override + public boolean hasNext() { + boolean next = mCursor.moveToNext(); + if (!next) { + mCursor.close(); + } + return next; + } + + @Override + public Cursor next() { + return mCursor; + } + } +} diff --git a/app/src/main/java/org/autojs/autojs/storage/database/IntentTaskDatabase.java b/app/src/main/java/org/autojs/autojs/storage/database/IntentTaskDatabase.java index 9ecc720d..9a8803d3 100644 --- a/app/src/main/java/org/autojs/autojs/storage/database/IntentTaskDatabase.java +++ b/app/src/main/java/org/autojs/autojs/storage/database/IntentTaskDatabase.java @@ -1,7 +1,64 @@ package org.autojs.autojs.storage.database; -import com.raizlabs.android.dbflow.annotation.Database; -@Database(version = 1) -public class IntentTaskDatabase { +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +import org.autojs.autojs.timing.IntentTask; + +public class IntentTaskDatabase extends Database { + + private static final int VERSION = 1; + private static final String NAME = "IntentTaskDatabase"; + + public IntentTaskDatabase(Context context){ + super(new SQLHelper(context), IntentTask.TABLE); + } + + @Override + protected ContentValues asContentValues(IntentTask model) { + ContentValues values = new ContentValues(); + values.put("script_path", model.getScriptPath()); + values.put("action", model.getAction()); + values.put("category", model.getCategory()); + values.put("data_type", model.getDataType()); + return values; + } + + @Override + protected IntentTask createModelFromCursor(Cursor cursor) { + IntentTask task = new IntentTask(); + task.setId(cursor.getInt(0)); + task.setScriptPath(cursor.getString(1)); + task.setAction(cursor.getString(2)); + task.setCategory(cursor.getString(3)); + task.setDataType(cursor.getString(4)); + return task; + } + + + private static class SQLHelper extends SQLiteOpenHelper { + + public SQLHelper(Context context) { + super(context, NAME + ".db", null, VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE `" + IntentTask.TABLE + "`(" + + "`id` INTEGER PRIMARY KEY AUTOINCREMENT, " + + "`script_path` TEXT NOT NULL ON CONFLICT FAIL, " + + "`action` TEXT, " + + "`category` TEXT, " + + "`data_type` TEXT);"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + + } + } } diff --git a/app/src/main/java/org/autojs/autojs/storage/database/ModelChange.java b/app/src/main/java/org/autojs/autojs/storage/database/ModelChange.java index 2f8c9ff2..4cb78f77 100644 --- a/app/src/main/java/org/autojs/autojs/storage/database/ModelChange.java +++ b/app/src/main/java/org/autojs/autojs/storage/database/ModelChange.java @@ -1,6 +1,5 @@ package org.autojs.autojs.storage.database; -import com.raizlabs.android.dbflow.structure.BaseModel; /** * Created by Stardust on 2017/11/28. @@ -8,10 +7,15 @@ import com.raizlabs.android.dbflow.structure.BaseModel; public class ModelChange { - private final M mData; - private final BaseModel.Action mAction; + public static final int INSERT = 1; + public static final int UPDATE = 2; + public static final int DELETE = 3; - public ModelChange(M data, BaseModel.Action action) { + + private final M mData; + private final int mAction; + + public ModelChange(M data, int action) { mData = data; mAction = action; } @@ -20,7 +24,7 @@ public class ModelChange { return mData; } - public BaseModel.Action getAction() { + public int getAction() { return mAction; } 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 8e3fc3c9..868de3df 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 @@ -1,12 +1,74 @@ package org.autojs.autojs.storage.database; -import com.raizlabs.android.dbflow.annotation.Database; -/** - * Created by Stardust on 2017/11/28. - */ -@Database(version = 1) -public class TimedTaskDatabase { +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; +import org.autojs.autojs.timing.TimedTask; + +public class TimedTaskDatabase extends Database { + + private static final int VERSION = 3; + private static final String NAME = "TimedTaskDatabase"; + + public TimedTaskDatabase(Context context) { + super(new SQLHelper(context), TimedTask.TABLE); + } + + @Override + protected ContentValues asContentValues(TimedTask model) { + ContentValues values = new ContentValues(); + values.put("time", model.getTimeFlag()); + values.put("scheduled", model.isScheduled()); + values.put("delay", model.getDelay()); + values.put("interval", model.getInterval()); + values.put("loop_times", model.getLoopTimes()); + values.put("millis", model.getMillis()); + values.put("script_path", model.getScriptPath()); + return values; + } + + @Override + protected TimedTask createModelFromCursor(Cursor cursor) { + TimedTask task = new TimedTask(); + task.setId(cursor.getInt(0)); + task.setTimeFlag(cursor.getLong(1)); + task.setScheduled(cursor.getInt(2) != 0); + task.setDelay(cursor.getLong(3)); + task.setInterval(cursor.getLong(4)); + task.setLoopTimes(cursor.getInt(5)); + task.setMillis(cursor.getLong(6)); + task.setScriptPath(cursor.getString(7)); + return task; + } + + + private static class SQLHelper extends SQLiteOpenHelper { + + public SQLHelper(Context context) { + super(context, NAME + ".db", null, VERSION); + } + + @Override + public void onCreate(SQLiteDatabase db) { + db.execSQL("CREATE TABLE `" + TimedTask.TABLE + "`(" + + "`id` INTEGER PRIMARY KEY AUTOINCREMENT, " + + "`time` INTEGER, " + + "`scheduled` INTEGER, " + + "`delay` INTEGER, " + + "`interval` INTEGER, " + + "`loop_times` INTEGER, " + + "`millis` INTEGER, " + + "`script_path` TEXT);"); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + + } + } } diff --git a/app/src/main/java/org/autojs/autojs/timing/IntentTask.java b/app/src/main/java/org/autojs/autojs/timing/IntentTask.java index 937e47fa..1937ea84 100644 --- a/app/src/main/java/org/autojs/autojs/timing/IntentTask.java +++ b/app/src/main/java/org/autojs/autojs/timing/IntentTask.java @@ -2,33 +2,19 @@ package org.autojs.autojs.timing; import android.content.IntentFilter; -import com.raizlabs.android.dbflow.annotation.Column; -import com.raizlabs.android.dbflow.annotation.NotNull; -import com.raizlabs.android.dbflow.annotation.PrimaryKey; -import com.raizlabs.android.dbflow.annotation.Table; +import org.autojs.autojs.storage.database.BaseModel; -import org.autojs.autojs.storage.database.IntentTaskDatabase; -import org.autojs.autojs.storage.database.TimedTaskDatabase; +public class IntentTask extends BaseModel { -@Table(database = IntentTaskDatabase.class) -public class IntentTask { + public static final String TABLE = "IntentTask"; - @PrimaryKey(autoincrement = true, quickCheckAutoIncrement = true) - @Column(name = "id") - int mId = -1; + private String mScriptPath; - @NotNull - @Column(name = "script_path") - String mScriptPath; + private String mAction; - @Column(name = "action") - String mAction; + private String mCategory; - @Column(name = "category") - String mCategory; - - @Column(name = "data_type") - String mDataType; + private String mDataType; public IntentFilter getIntentFilter() { IntentFilter filter = new IntentFilter(); @@ -48,14 +34,6 @@ public class IntentTask { return filter; } - public int getId() { - return mId; - } - - public void setId(int id) { - mId = id; - } - public String getScriptPath() { return mScriptPath; } diff --git a/app/src/main/java/org/autojs/autojs/timing/TaskReceiver.java b/app/src/main/java/org/autojs/autojs/timing/TaskReceiver.java index 56cf7f31..85a618e3 100644 --- a/app/src/main/java/org/autojs/autojs/timing/TaskReceiver.java +++ b/app/src/main/java/org/autojs/autojs/timing/TaskReceiver.java @@ -20,7 +20,7 @@ public class TaskReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { ScriptIntents.handleIntent(context, intent); - int id = intent.getIntExtra(EXTRA_TASK_ID, -1); + long id = intent.getLongExtra(EXTRA_TASK_ID, -1); if (id >= 0) { TimedTaskManager.getInstance().notifyTaskFinished(id); } diff --git a/app/src/main/java/org/autojs/autojs/timing/TimedTask.java b/app/src/main/java/org/autojs/autojs/timing/TimedTask.java index 44999aee..63a861ac 100644 --- a/app/src/main/java/org/autojs/autojs/timing/TimedTask.java +++ b/app/src/main/java/org/autojs/autojs/timing/TimedTask.java @@ -4,13 +4,10 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import com.raizlabs.android.dbflow.annotation.Column; -import com.raizlabs.android.dbflow.annotation.PrimaryKey; -import com.raizlabs.android.dbflow.annotation.Table; import com.stardust.autojs.execution.ExecutionConfig; -import org.autojs.autojs.external.ScriptIntents; -import org.autojs.autojs.storage.database.TimedTaskDatabase; +import org.autojs.autojs.external.ScriptIntents; +import org.autojs.autojs.storage.database.BaseModel; import org.joda.time.DateTime; import org.joda.time.DateTimeConstants; import org.joda.time.LocalDateTime; @@ -19,11 +16,9 @@ import org.joda.time.LocalTime; import java.util.concurrent.TimeUnit; -/** - * Created by Stardust on 2017/11/27. - */ -@Table(database = TimedTaskDatabase.class) -public class TimedTask { +public class TimedTask extends BaseModel { + + public static final String TABLE = "TimedTask"; private static final int FLAG_DISPOSABLE = 0; public final static int FLAG_SUNDAY = 0x1; @@ -36,29 +31,18 @@ public class TimedTask { private static final int FLAG_EVERYDAY = 0x7F; private static final int REQUEST_CODE = 2000; - @PrimaryKey(autoincrement = true, quickCheckAutoIncrement = true) - @Column(name = "id") - int mId = -1; - - @Column(name = "time") long mTimeFlag; - @Column(name = "scheduled") boolean mScheduled; - @Column(name = "delay") long mDelay = 0; - @Column(name = "interval") long mInterval = 0; - @Column(name = "loop_times") int mLoopTimes = 1; - @Column(name = "millis") long mMillis; - @Column(name = "script_path") String mScriptPath; public TimedTask() { @@ -148,19 +132,10 @@ public class TimedTask { return mMillis; } - public int getId() { - return mId; - } - public String getScriptPath() { return mScriptPath; } - - public void setId(int id) { - mId = id; - } - public long getTimeFlag() { return mTimeFlag; } @@ -207,7 +182,7 @@ public class TimedTask { public Intent createIntent() { return new Intent(TaskReceiver.ACTION_TASK) - .putExtra(TaskReceiver.EXTRA_TASK_ID, mId) + .putExtra(TaskReceiver.EXTRA_TASK_ID, getId()) .putExtra(ScriptIntents.EXTRA_KEY_PATH, mScriptPath) .putExtra(ScriptIntents.EXTRA_KEY_DELAY, mDelay) .putExtra(ScriptIntents.EXTRA_KEY_LOOP_TIMES, mLoopTimes) @@ -216,14 +191,14 @@ public class TimedTask { public PendingIntent createPendingIntent(Context context) { - return PendingIntent.getBroadcast(context, REQUEST_CODE + 1 + getId(), + return PendingIntent.getBroadcast(context, (int) ((REQUEST_CODE + 1 + getId()) % 65535), createIntent(), PendingIntent.FLAG_UPDATE_CURRENT); } @Override public String toString() { return "TimedTask{" + - "mId=" + mId + + "mId=" + getId() + ", mTimeFlag=" + mTimeFlag + ", mScheduled=" + mScheduled + ", mDelay=" + mDelay + @@ -234,20 +209,6 @@ public class TimedTask { '}'; } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - TimedTask timedTask = (TimedTask) o; - - return mId == timedTask.mId; - } - - @Override - public int hashCode() { - return mId; - } public static TimedTask dailyTask(LocalTime time, String scriptPath, ExecutionConfig config) { return new TimedTask(time.getMillisOfDay(), FLAG_EVERYDAY, scriptPath, config); 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 9640c9e3..97169850 100644 --- a/app/src/main/java/org/autojs/autojs/timing/TimedTaskManager.java +++ b/app/src/main/java/org/autojs/autojs/timing/TimedTaskManager.java @@ -1,28 +1,20 @@ package org.autojs.autojs.timing; +import android.annotation.SuppressLint; import android.content.Context; -import android.content.Intent; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import com.raizlabs.android.dbflow.config.FlowManager; -import com.raizlabs.android.dbflow.runtime.DirectModelNotifier; -import com.raizlabs.android.dbflow.rx2.language.RXSQLite; -import com.raizlabs.android.dbflow.sql.language.SQLite; -import com.raizlabs.android.dbflow.structure.BaseModel; -import com.raizlabs.android.dbflow.structure.ModelAdapter; import com.stardust.app.GlobalAppContext; -import org.autojs.autojs.Pref; +import org.autojs.autojs.storage.database.IntentTaskDatabase; import org.autojs.autojs.storage.database.ModelChange; +import org.autojs.autojs.storage.database.TimedTaskDatabase; +import org.autojs.autojs.tool.EmptyObservers; -import java.io.File; import java.util.List; import io.reactivex.Flowable; import io.reactivex.Observable; -import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.PublishSubject; /** @@ -31,12 +23,10 @@ import io.reactivex.subjects.PublishSubject; //TODO rx public class TimedTaskManager { - private static TimedTaskManager sInstance; - private ModelAdapter mTimedTaskModelAdapter; - private ModelAdapter mIntentTaskModelAdapter; private Context mContext; - private PublishSubject> mTimedTaskChanges = PublishSubject.create(); + private TimedTaskDatabase mTimedTaskDatabase; + private IntentTaskDatabase mIntentTaskDatabase; public static TimedTaskManager getInstance() { if (sInstance == null) { @@ -45,104 +35,108 @@ public class TimedTaskManager { return sInstance; } + @SuppressLint("CheckResult") public TimedTaskManager(Context context) { mContext = context; - mTimedTaskModelAdapter = FlowManager.getModelAdapter(TimedTask.class); - mIntentTaskModelAdapter = FlowManager.getModelAdapter(IntentTask.class); - DirectModelNotifier.get().registerForModelChanges(TimedTask.class, new DirectModelNotifier.ModelChangedListener() { - @Override - public void onModelChanged(@NonNull TimedTask model, @NonNull BaseModel.Action action) { - mTimedTaskChanges.onNext(new ModelChange<>(model, action)); - if (action == BaseModel.Action.DELETE && countTasks() == 0) { - TimedTaskScheduler.stopRtcRepeating(mContext); - } else if (action == BaseModel.Action.INSERT) { - TimedTaskScheduler.checkTasksRepeatedlyIfNeeded(mContext); - } - } - - @Override - public void onTableChanged(@Nullable Class tableChanged, @NonNull BaseModel.Action action) { - + 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); } }); } - public void notifyTaskFinished(int id) { + @SuppressLint("CheckResult") + public void notifyTaskFinished(long id) { TimedTask task = getTimedTask(id); if (task == null) return; if (task.isDisposable()) { - mTimedTaskModelAdapter.delete(task); + mTimedTaskDatabase.delete(task) + .subscribe(EmptyObservers.consumer(), Throwable::printStackTrace); } else { task.setScheduled(false); - mTimedTaskModelAdapter.update(task); + mTimedTaskDatabase.update(task) + .subscribe(EmptyObservers.consumer(), Throwable::printStackTrace); } } + @SuppressLint("CheckResult") public void removeTask(TimedTask timedTask) { TimedTaskScheduler.cancel(mContext, timedTask); - mTimedTaskModelAdapter.delete(timedTask); + mTimedTaskDatabase.delete(timedTask) + .subscribe(EmptyObservers.consumer(), Throwable::printStackTrace); } + @SuppressLint("CheckResult") public void addTask(TimedTask timedTask) { - mTimedTaskModelAdapter.insert(timedTask); + mTimedTaskDatabase.insert(timedTask) + .subscribe(EmptyObservers.consumer(), Throwable::printStackTrace);; TimedTaskScheduler.scheduleTaskIfNeeded(mContext, timedTask); } + @SuppressLint("CheckResult") public void addTask(IntentTask intentTask) { - mIntentTaskModelAdapter.insert(intentTask); + mIntentTaskDatabase.insert(intentTask) + .subscribe(EmptyObservers.consumer(), Throwable::printStackTrace); } + @SuppressLint("CheckResult") public void removeTask(IntentTask intentTask) { - mIntentTaskModelAdapter.delete(intentTask); + mIntentTaskDatabase.delete(intentTask) + .subscribe(EmptyObservers.consumer(), Throwable::printStackTrace);; } public Flowable getAllTasks() { - return RXSQLite.rx(SQLite.select().from(TimedTask.class)) - .queryStreamResults() - .subscribeOn(Schedulers.io()); + return mTimedTaskDatabase.queryAllAsFlowable(); } public Flowable getIntentTaskOfAction(String action) { - IntentTask intentTask = new IntentTask(); - intentTask.setAction(Intent.ACTION_BOOT_COMPLETED); - intentTask.setScriptPath(new File(Pref.getScriptDirPath(), "boot.js").getPath()); - return Flowable.just(intentTask); -// return RXSQLite.rx(SQLite.select().from(IntentTask.class)) -// .queryStreamResults() -// .subscribeOn(Schedulers.io()); + return mIntentTaskDatabase.query("action = ?", action); } public Observable> getTimeTaskChanges() { - return mTimedTaskChanges; + return mTimedTaskDatabase.getModelChange(); } + @SuppressLint("CheckResult") public void notifyTaskScheduled(TimedTask timedTask) { timedTask.setScheduled(true); - mTimedTaskModelAdapter.update(timedTask); + mTimedTaskDatabase.update(timedTask) + .subscribe(EmptyObservers.consumer(), Throwable::printStackTrace); } public List getAllTasksAsList() { - return SQLite.select().from(TimedTask.class) - .queryList(); + return mTimedTaskDatabase.queryAll(); } - public TimedTask getTimedTask(int taskId) { - return SQLite.select() - .from(TimedTask.class) - .where(TimedTask_Table.id.is(taskId)) - .querySingle(); + public TimedTask getTimedTask(long taskId) { + return mTimedTaskDatabase.queryById(taskId); } + @SuppressLint("CheckResult") public void updateTask(TimedTask task) { - mTimedTaskModelAdapter.update(task); + mTimedTaskDatabase.update(task) + .subscribe(EmptyObservers.consumer(), Throwable::printStackTrace); TimedTaskScheduler.cancel(mContext, task); TimedTaskScheduler.scheduleTaskIfNeeded(mContext, task); } public long countTasks() { - return SQLite.select().from(TimedTask.class).count(); + return mTimedTaskDatabase.count(); + } + + public List getAllIntentTasksAsList() { + return mIntentTaskDatabase.queryAll(); + } + + public Observable> getIntentTaskChanges() { + return mIntentTaskDatabase.getModelChange(); } } 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 5373d21c..62356666 100644 --- a/app/src/main/java/org/autojs/autojs/timing/TimedTaskScheduler.java +++ b/app/src/main/java/org/autojs/autojs/timing/TimedTaskScheduler.java @@ -28,7 +28,6 @@ public class TimedTaskScheduler extends BroadcastReceiver { private static final long INTERVAL = TimeUnit.MINUTES.toMillis(1); private static final long ONE_HOUR = TimeUnit.HOURS.toMillis(1); private static PendingIntent sCheckTasksPendingIntent; - private static long mNextRtcWakeupMillis = -1; @Override public void onReceive(Context context, Intent intent) { @@ -38,7 +37,7 @@ public class TimedTaskScheduler extends BroadcastReceiver { } @SuppressLint("CheckResult") - private static void checkTasks(Context context) { + public static void checkTasks(Context context) { TimedTaskManager.getInstance().getAllTasks() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -83,7 +82,7 @@ public class TimedTaskScheduler extends BroadcastReceiver { public static void checkTasksRepeatedlyIfNeeded(Context context) { - if (TimedTaskManager.getInstance().countTasks() > 0 && mNextRtcWakeupMillis > 0) { + if (TimedTaskManager.getInstance().countTasks() > 0) { setupNextRtcWakeup(context, System.currentTimeMillis() + 5000); } } @@ -95,7 +94,6 @@ public class TimedTaskScheduler extends BroadcastReceiver { } AlarmManager alarmManager = getAlarmManager(context); setExactCompat(alarmManager, createTaskCheckPendingIntent(context), millis); - mNextRtcWakeupMillis = millis; } diff --git a/app/src/main/java/org/autojs/autojs/ui/common/ScriptOperations.java b/app/src/main/java/org/autojs/autojs/ui/common/ScriptOperations.java index 5d82198b..5803da69 100644 --- a/app/src/main/java/org/autojs/autojs/ui/common/ScriptOperations.java +++ b/app/src/main/java/org/autojs/autojs/ui/common/ScriptOperations.java @@ -22,6 +22,7 @@ import com.stardust.app.GlobalAppContext; import com.stardust.pio.PFile; import com.stardust.pio.PFiles; import com.stardust.pio.UncheckedIOException; +import com.tencent.bugly.crashreport.BuglyLog; import org.autojs.autojs.Pref; import org.autojs.autojs.R; @@ -319,6 +320,7 @@ public class ScriptOperations { public Observable download(String url) { + BuglyLog.i(LOG_TAG, "dir = " + Pref.getScriptDirPath() + ", sdcard = " + Environment.getExternalStorageDirectory() + ", url = " + url); String fileName = DownloadManager.parseFileNameLocally(url); return new FileChooserDialogBuilder(mContext) .title(R.string.text_select_save_path) diff --git a/app/src/main/java/org/autojs/autojs/ui/filechooser/FileChooserDialogBuilder.java b/app/src/main/java/org/autojs/autojs/ui/filechooser/FileChooserDialogBuilder.java index 7021f7d0..abd73d02 100644 --- a/app/src/main/java/org/autojs/autojs/ui/filechooser/FileChooserDialogBuilder.java +++ b/app/src/main/java/org/autojs/autojs/ui/filechooser/FileChooserDialogBuilder.java @@ -6,6 +6,7 @@ import android.support.annotation.StringRes; import com.afollestad.materialdialogs.MaterialDialog; import com.stardust.pio.PFile; +import com.tencent.bugly.crashreport.BuglyLog; import org.autojs.autojs.R; import org.autojs.autojs.model.explorer.Explorer; @@ -66,7 +67,6 @@ public class FileChooserDialogBuilder extends ThemeColorMaterialDialogBuilder { public FileChooserDialogBuilder dir(String rootDir, String initialDir) { mRootDir = rootDir; mInitialDir = initialDir; - return this; } diff --git a/app/src/main/java/org/autojs/autojs/ui/main/task/Task.java b/app/src/main/java/org/autojs/autojs/ui/main/task/Task.java index a6ed37aa..4e5e03f4 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/task/Task.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/task/Task.java @@ -8,6 +8,7 @@ import com.stardust.autojs.script.JavaScriptSource; import com.stardust.pio.PFiles; import org.autojs.autojs.R; +import org.autojs.autojs.timing.IntentTask; import org.autojs.autojs.timing.TimedTask; import org.autojs.autojs.timing.TimedTaskManager; @@ -30,10 +31,24 @@ public abstract class Task { public static class PendingTask extends Task { private TimedTask mTimedTask; + private IntentTask mIntentTask; public PendingTask(TimedTask timedTask) { mTimedTask = timedTask; + mIntentTask = null; + } + + public PendingTask(IntentTask intentTask) { + mIntentTask = intentTask; + mTimedTask = null; + } + + public boolean taskEquals(Object task){ + if(mTimedTask != null){ + return mTimedTask.equals(task); + } + return mIntentTask.equals(task); } public TimedTask getTimedTask() { @@ -42,14 +57,20 @@ public abstract class Task { @Override public String getName() { - return PFiles.getSimplifiedPath(mTimedTask.getScriptPath()); + return PFiles.getSimplifiedPath(getScriptPath()); } @Override public String getDesc() { - long nextTime = mTimedTask.getNextTime(); - return GlobalAppContext.getString(R.string.text_next_run_time) + ": " + - DateTimeFormat.shortDateTime().print(nextTime); + if (mTimedTask != null) { + long nextTime = mTimedTask.getNextTime(); + return GlobalAppContext.getString(R.string.text_next_run_time) + ": " + + DateTimeFormat.shortDateTime().print(nextTime); + } else { + assert mIntentTask != null; + return mIntentTask.getAction(); + } + } @Override @@ -57,9 +78,18 @@ public abstract class Task { TimedTaskManager.getInstance().removeTask(mTimedTask); } + private String getScriptPath() { + if (mTimedTask != null) { + return mTimedTask.getScriptPath(); + } else { + assert mIntentTask != null; + return mIntentTask.getScriptPath(); + } + } + @Override public String getEngineName() { - if (mTimedTask.getScriptPath().endsWith(".js")) { + if (getScriptPath().endsWith(".js")) { return JavaScriptSource.ENGINE; } else { return AutoFileSource.ENGINE; @@ -69,6 +99,10 @@ public abstract class Task { public void setTimedTask(TimedTask timedTask) { mTimedTask = timedTask; } + + public void setIntentTask(IntentTask intentTask) { + mIntentTask = intentTask; + } } public static class RunningTask extends Task { diff --git a/app/src/main/java/org/autojs/autojs/ui/main/task/TaskGroup.java b/app/src/main/java/org/autojs/autojs/ui/main/task/TaskGroup.java index a1aef3d6..8a4f7e68 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/task/TaskGroup.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/task/TaskGroup.java @@ -8,6 +8,7 @@ import com.stardust.autojs.execution.ScriptExecution; import org.autojs.autojs.R; import org.autojs.autojs.autojs.AutoJs; +import org.autojs.autojs.timing.IntentTask; import org.autojs.autojs.timing.TimedTask; import org.autojs.autojs.timing.TimedTaskManager; @@ -55,39 +56,55 @@ public abstract class TaskGroup implements Parent { @Override public void refresh() { - List timedTasks = TimedTaskManager.getInstance().getAllTasksAsList(); - mTasks.clear(); - for (TimedTask timedTask : timedTasks) { + for (TimedTask timedTask : TimedTaskManager.getInstance().getAllTasksAsList()) { mTasks.add(new Task.PendingTask(timedTask)); } + for (IntentTask intentTask : TimedTaskManager.getInstance().getAllIntentTasksAsList()) { + mTasks.add(new Task.PendingTask(intentTask)); + } } - public int addTask(TimedTask timedTask) { + public int addTask(Object task) { int pos = mTasks.size(); - mTasks.add(new Task.PendingTask(timedTask)); + if (task instanceof TimedTask) { + mTasks.add(new Task.PendingTask((TimedTask) task)); + } else if (task instanceof IntentTask) { + mTasks.add(new Task.PendingTask((IntentTask) task)); + } else { + throw new IllegalArgumentException("task = " + task); + } return pos; } - public int removeTask(TimedTask data) { + public int removeTask(Object data) { int i = indexOf(data); if (i >= 0) mTasks.remove(i); return i; } - private int indexOf(TimedTask data) { + private int indexOf(Object data) { for (int i = 0; i < mTasks.size(); i++) { - if (((Task.PendingTask) mTasks.get(i)).getTimedTask().equals(data)) { + Task.PendingTask task = (Task.PendingTask) mTasks.get(i); + if (task.taskEquals(data)) { return i; } } return -1; } - public int updateTask(TimedTask task) { + + public int updateTask(Object task) { int i = indexOf(task); - if (i >= 0) - ((Task.PendingTask) mTasks.get(i)).setTimedTask(task); + if (i >= 0) { + if (task instanceof TimedTask) { + ((Task.PendingTask) mTasks.get(i)).setTimedTask((TimedTask) task); + } else if (task instanceof IntentTask) { + ((Task.PendingTask) mTasks.get(i)).setIntentTask((IntentTask) task); + } else { + throw new IllegalArgumentException("task = " + task); + } + } return i; } } diff --git a/app/src/main/java/org/autojs/autojs/ui/main/task/TaskListRecyclerView.java b/app/src/main/java/org/autojs/autojs/ui/main/task/TaskListRecyclerView.java index aba82132..6825c9fe 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/task/TaskListRecyclerView.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/task/TaskListRecyclerView.java @@ -13,30 +13,27 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; -import com.stardust.autojs.workground.WrapContentLinearLayoutManager; - import com.bignerdranch.expandablerecyclerview.ChildViewHolder; import com.bignerdranch.expandablerecyclerview.ExpandableRecyclerAdapter; import com.bignerdranch.expandablerecyclerview.ParentViewHolder; -import com.raizlabs.android.dbflow.structure.BaseModel; import com.stardust.autojs.ScriptEngineService; -import com.stardust.autojs.engine.ScriptEngineManager; import com.stardust.autojs.execution.ScriptExecution; import com.stardust.autojs.execution.ScriptExecutionListener; import com.stardust.autojs.execution.SimpleScriptExecutionListener; -import com.stardust.autojs.engine.ScriptEngine; import com.stardust.autojs.script.AutoFileSource; +import com.stardust.autojs.workground.WrapContentLinearLayoutManager; +import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; import org.autojs.autojs.R; import org.autojs.autojs.autojs.AutoJs; +import org.autojs.autojs.external.tasker.TaskerScriptEditActivity; import org.autojs.autojs.storage.database.ModelChange; +import org.autojs.autojs.timing.IntentTask; import org.autojs.autojs.timing.TaskReceiver; import org.autojs.autojs.timing.TimedTask; import org.autojs.autojs.timing.TimedTaskManager; import org.autojs.autojs.ui.timing.TimedTaskSettingActivity_; -import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; - import java.util.ArrayList; import java.util.List; @@ -60,7 +57,7 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView { private TaskGroup.PendingTaskGroup mPendingTaskGroup; private Adapter mAdapter; private Disposable mTimedTaskChangeDisposable; - private final ScriptEngineService mScriptEngineService = AutoJs.getInstance().getScriptEngineService(); + private Disposable mIntentTaskChangeDisposable; private ScriptExecutionListener mScriptExecutionListener = new SimpleScriptExecutionListener() { @Override public void onStart(final ScriptExecution execution) { @@ -135,7 +132,10 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView { AutoJs.getInstance().getScriptEngineService().registerGlobalScriptExecutionListener(mScriptExecutionListener); mTimedTaskChangeDisposable = TimedTaskManager.getInstance().getTimeTaskChanges() .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::onTimedTaskChange); + .subscribe(this::onTaskChange); + mIntentTaskChangeDisposable = TimedTaskManager.getInstance().getIntentTaskChanges() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(this::onTaskChange); } @Override @@ -151,26 +151,25 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView { super.onDetachedFromWindow(); AutoJs.getInstance().getScriptEngineService().unregisterGlobalScriptExecutionListener(mScriptExecutionListener); mTimedTaskChangeDisposable.dispose(); + mIntentTaskChangeDisposable.dispose(); } - void onTimedTaskChange(ModelChange taskChange) { - if (taskChange.getAction() == BaseModel.Action.INSERT) { + void onTaskChange(ModelChange taskChange) { + if (taskChange.getAction() == ModelChange.INSERT) { mAdapter.notifyChildInserted(1, mPendingTaskGroup.addTask(taskChange.getData())); - } else if (taskChange.getAction() == BaseModel.Action.DELETE) { + } else if (taskChange.getAction() == ModelChange.DELETE) { final int i = mPendingTaskGroup.removeTask(taskChange.getData()); - // FIXME: 2017/11/28 task id is always 0 if (i >= 0) { mAdapter.notifyChildRemoved(1, i); } else { Log.w(LOG_TAG, "data inconsistent on change: " + taskChange); refresh(); } - } else if (taskChange.getAction() == BaseModel.Action.UPDATE) { + } else if (taskChange.getAction() == ModelChange.UPDATE) { final int i = mPendingTaskGroup.updateTask(taskChange.getData()); if (i >= 0) { mAdapter.notifyChildChanged(1, i); } else { - Log.w(LOG_TAG, "data inconsistent on change: " + taskChange); refresh(); } } @@ -250,7 +249,7 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView { void onItemClick(View view) { if (mTask instanceof Task.PendingTask) { TimedTaskSettingActivity_.intent(getContext()) - .extra(TaskReceiver.EXTRA_TASK_ID, ((Task.PendingTask) mTask).getTimedTask().getId()) + .extra(TaskerScriptEditActivity.EXTRA_TASK_ID, ((Task.PendingTask) mTask).getTimedTask().getId()) .start(); } } @@ -263,8 +262,8 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView { TaskGroupViewHolder(@NonNull View itemView) { super(itemView); - title = (TextView) itemView.findViewById(R.id.title); - icon = (ImageView) itemView.findViewById(R.id.icon); + title = itemView.findViewById(R.id.title); + icon = itemView.findViewById(R.id.icon); itemView.setOnClickListener(view -> { if (isExpanded()) { collapseView(); diff --git a/app/src/main/java/org/autojs/autojs/ui/timing/TimedTaskSettingActivity.java b/app/src/main/java/org/autojs/autojs/ui/timing/TimedTaskSettingActivity.java index b62898b4..130c49cf 100644 --- a/app/src/main/java/org/autojs/autojs/ui/timing/TimedTaskSettingActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/timing/TimedTaskSettingActivity.java @@ -18,6 +18,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.CompoundButton; +import android.widget.EditText; import android.widget.LinearLayout; import android.widget.RadioButton; import android.widget.RadioGroup; @@ -27,6 +28,9 @@ import android.widget.Toast; import com.github.aakira.expandablelayout.ExpandableRelativeLayout; import com.stardust.autojs.execution.ExecutionConfig; +import com.stardust.util.BiMap; +import com.stardust.util.BiMaps; +import com.stardust.util.MapEntries; import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.CheckedChange; @@ -49,6 +53,7 @@ import org.joda.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; +import java.util.Map; /** * Created by Stardust on 2017/11/28. @@ -62,6 +67,13 @@ public class TimedTaskSettingActivity extends BaseActivity { private static final int REQUEST_CODE_IGNORE_BATTERY = 27101; private static final String LOG_TAG = "TimedTaskSettings"; + private static final BiMap ACTIONS = BiMaps.newBuilder() + .put(R.id.run_on_boot, Intent.ACTION_BOOT_COMPLETED) + .put(R.id.run_on_screen_off, Intent.ACTION_SCREEN_OFF) + .put(R.id.run_on_screen_on, Intent.ACTION_SCREEN_ON) + .build(); + + @ViewById(R.id.toolbar) Toolbar mToolbar; @@ -77,9 +89,14 @@ public class TimedTaskSettingActivity extends BaseActivity { @ViewById(R.id.weekly_task_radio) RadioButton mWeeklyTaskRadio; - @ViewById(R.id.run_on_boot_radio) - RadioButton mRunOnBootRadio; + @ViewById(R.id.run_on_broadcast) + RadioButton mRunOnBroadcast; + @ViewById(R.id.action) + EditText mOtherBroadcastAction; + + @ViewById(R.id.broadcast_group) + RadioGroup mBroadcastGroup; @ViewById(R.id.disposable_task_time) TextView mDisposableTaskTime; @@ -98,14 +115,13 @@ public class TimedTaskSettingActivity extends BaseActivity { private List mDayOfWeekCheckBoxes = new ArrayList<>(); - private ScriptFile mScriptFile; private TimedTask mTimedTask; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - int taskId = getIntent().getIntExtra(TaskReceiver.EXTRA_TASK_ID, -1); + long taskId = getIntent().getLongExtra(TaskReceiver.EXTRA_TASK_ID, -1); if (taskId != -1) { mTimedTask = TimedTaskManager.getInstance().getTimedTask(taskId); if (mTimedTask != null) { @@ -175,7 +191,7 @@ public class TimedTaskSettingActivity extends BaseActivity { } - @CheckedChange({R.id.daily_task_radio, R.id.weekly_task_radio, R.id.disposable_task_radio}) + @CheckedChange({R.id.daily_task_radio, R.id.weekly_task_radio, R.id.disposable_task_radio, R.id.run_on_broadcast}) void onCheckedChanged(CompoundButton button) { ExpandableRelativeLayout relativeLayout = findExpandableLayoutOf(button); if (button.isChecked()) { @@ -271,8 +287,6 @@ public class TimedTaskSettingActivity extends BaseActivity { } else { createOrUpdateTimedTask(); } - - return true; } return super.onOptionsItemSelected(item); @@ -288,12 +302,8 @@ public class TimedTaskSettingActivity extends BaseActivity { } private void createOrUpdateTimedTask() { - if (mRunOnBootRadio.isChecked()) { - IntentTask task = new IntentTask(); - task.setAction(Intent.ACTION_BOOT_COMPLETED); - task.setScriptPath(mScriptFile.getPath()); - TimedTaskManager.getInstance().addTask(task); - finish(); + if (mRunOnBroadcast.isChecked()) { + createIntentTask(); return; } TimedTask task = createTimedTask(); @@ -308,4 +318,24 @@ public class TimedTaskSettingActivity extends BaseActivity { } finish(); } + + + private void createIntentTask() { + int buttonId = mBroadcastGroup.getCheckedRadioButtonId(); + String action; + if(buttonId == R.id.run_on_other_broadcast){ + action = mOtherBroadcastAction.getText().toString(); + if(action.isEmpty()){ + mOtherBroadcastAction.setError(getString(R.string.text_should_not_be_empty)); + return; + } + }else { + action = ACTIONS.get(buttonId); + } + IntentTask task = new IntentTask(); + task.setAction(action); + task.setScriptPath(mScriptFile.getPath()); + TimedTaskManager.getInstance().addTask(task); + finish(); + } } diff --git a/app/src/main/res/layout/activity_timed_task_setting.xml b/app/src/main/res/layout/activity_timed_task_setting.xml index b72f9cca..4d90b0e8 100644 --- a/app/src/main/res/layout/activity_timed_task_setting.xml +++ b/app/src/main/res/layout/activity_timed_task_setting.xml @@ -253,12 +253,86 @@ + android:text="@string/text_run_on_broadcast"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0bebb257..0f1f535d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -382,7 +382,7 @@ 使用系统闹钟唤醒Auto.js 连接失败: %s 确定要删除%s吗 - 开机时运行 + 特定事件(广播)触发运行 搜索Java包/类 类/包名 查看文档 @@ -403,4 +403,10 @@ 前台服务通知 Auto.js保持运行中 点击进入主界面 + 开机时运行 + 亮屏时运行 + 息屏时运行 + 其他事件(广播) + 广播Action + android.intent.action. diff --git a/common/src/main/java/com/stardust/util/BiMap.java b/common/src/main/java/com/stardust/util/BiMap.java new file mode 100644 index 00000000..65420801 --- /dev/null +++ b/common/src/main/java/com/stardust/util/BiMap.java @@ -0,0 +1,11 @@ +package com.stardust.util; + +import java.util.Map; +import java.util.Set; + +public interface BiMap extends Map { + + K getKey(V value); + + Set valueSet(); +} diff --git a/common/src/main/java/com/stardust/util/BiMaps.java b/common/src/main/java/com/stardust/util/BiMaps.java new file mode 100644 index 00000000..a809c5b4 --- /dev/null +++ b/common/src/main/java/com/stardust/util/BiMaps.java @@ -0,0 +1,206 @@ +package com.stardust.util; + +import android.os.Build; +import android.support.annotation.NonNull; +import android.support.annotation.RequiresApi; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Function; + +public class BiMaps { + + public static BiMap make(Map keyToValue, Map valueToKey) { + return new BiMapImpl<>(keyToValue, valueToKey); + } + + public static BiMapBuilder newBuilder() { + return new BiMapBuilder<>(); + } + + public static class BiMapBuilder { + + private final BiMap mBiMap = make(new HashMap(), new HashMap()); + + public BiMapBuilder put(K key, V value) { + mBiMap.put(key, value); + return this; + } + + public BiMap build() { + return mBiMap; + } + + } + + private static class BiMapImpl implements BiMap { + + private final Map mKVMap; + private final Map mVKMap; + + private BiMapImpl(Map kvMap, Map vkMap) { + mKVMap = kvMap; + mVKMap = vkMap; + } + + @Override + public int size() { + return mKVMap.size(); + } + + @Override + public boolean isEmpty() { + return mKVMap.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return mKVMap.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return mVKMap.containsKey(value); + } + + @Override + public V get(Object key) { + return mKVMap.get(key); + } + + @Override + public V put(K key, V value) { + V put = mKVMap.put(key, value); + mVKMap.put(value, key); + return put; + } + + @Override + public V remove(Object key) { + V remove = mKVMap.remove(key); + if (remove != null) { + mVKMap.remove(key); + } + return remove; + } + + @Override + public void putAll(@NonNull Map m) { + mKVMap.putAll(m); + } + + @Override + public void clear() { + mKVMap.clear(); + mVKMap.clear(); + } + + @NonNull + @Override + public Set keySet() { + return mKVMap.keySet(); + } + + @Override + public K getKey(V value) { + return mVKMap.get(value); + } + + @Override + public Set valueSet() { + return mVKMap.keySet(); + } + + @NonNull + @Override + public Collection values() { + return mKVMap.values(); + } + + @NonNull + @Override + public Set> entrySet() { + return mKVMap.entrySet(); + } + + @Override + public boolean equals(Object o) { + return mKVMap.equals(o); + } + + @Override + public int hashCode() { + return mKVMap.hashCode(); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public V getOrDefault(Object key, V defaultValue) { + return mKVMap.getOrDefault(key, defaultValue); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public void forEach(BiConsumer action) { + mKVMap.forEach(action); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public void replaceAll(BiFunction function) { + mKVMap.replaceAll(function); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public V putIfAbsent(K key, V value) { + return mKVMap.putIfAbsent(key, value); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public boolean remove(Object key, Object value) { + return mKVMap.remove(key, value); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public boolean replace(K key, V oldValue, V newValue) { + return mKVMap.replace(key, oldValue, newValue); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public V replace(K key, V value) { + return mKVMap.replace(key, value); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public V computeIfAbsent(K key, Function mappingFunction) { + return mKVMap.computeIfAbsent(key, mappingFunction); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public V computeIfPresent(K key, BiFunction remappingFunction) { + return mKVMap.computeIfPresent(key, remappingFunction); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public V compute(K key, BiFunction remappingFunction) { + return mKVMap.compute(key, remappingFunction); + } + + @RequiresApi(api = Build.VERSION_CODES.N) + @Override + public V merge(K key, V value, BiFunction remappingFunction) { + return mKVMap.merge(key, value, remappingFunction); + } + } +}