feat: intent task

This commit is contained in:
hyb1996 2018-09-28 00:56:34 +08:00
parent cf16739e75
commit 025e594a01
29 changed files with 1008 additions and 239 deletions

View File

@ -3,6 +3,7 @@
<words>
<w>capturer</w>
<w>dismissable</w>
<w>flowable</w>
<w>interruptible</w>
<w>loopers</w>
<w>prefill</w>

View File

@ -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

View File

@ -133,10 +133,36 @@
</intent-filter>
</activity-alias>
<receiver android:name="org.autojs.autojs.external.boot.BootCompleteReceiver" >
<receiver android:name="org.autojs.autojs.external.receiver.StaticBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.QUICKBOOT_POWERON" />
<action android:name="android.intent.action.TIME_SET"/>
<action android:name="android.intent.action.TIMEZONE_CHANGED"/>
<action android:name="android.intent.action.PACKAGE_ADDED"/>
<action android:name="android.intent.action.PACKAGE_CHANGED"/>
<action android:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
<action android:name="android.intent.action.PACKAGE_REMOVED"/>
<action android:name="android.intent.action.PACKAGE_RESTARTED"/>
<action android:name="android.intent.action.UID_REMOVED"/>
<action android:name="android.intent.action.ACTION_POWER_CONNECTED"/>
<action android:name="android.intent.action.ACTION_POWER_DISCONNECTED"/>
<action android:name="android.intent.action.ACTION_SHUTDOWN"/>
<action android:name="android.intent.action.DATE_CHANGED"/>
<action android:name="android.intent.action.DREAMING_STARTED"/>
<action android:name="android.intent.action.DREAMING_STOPPED"/>
<action android:name="android.intent.action.HEADSET_PLUG"/>
<action android:name="android.intent.action.INPUT_METHOD_CHANGED"/>
<action android:name="android.intent.action.LOCALE_CHANGED"/>
<action android:name="android.intent.action.MEDIA_BUTTON"/>
<action android:name="android.intent.action.MEDIA_CHECKING"/>
<action android:name="android.intent.action.MEDIA_MOUNTED"/>
<action android:name="android.intent.action.PACKAGE_FIRST_LAUNCH"/>
<action android:name="android.intent.action.PROVIDER_CHANGED"/>
<action android:name="android.intent.action.WALLPAPER_CHANGED"/>
<action android:name="android.intent.action.USER_UNLOCKED"/>
<action android:name="android.intent.action.USER_PRESENT"/>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
</receiver>

View File

@ -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<App> 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() {

View File

@ -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();
}
}
}

View File

@ -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<String> 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<String> mActions = new LinkedHashSet<>();
private final Context mContext;
public DynamicBroadcastReceiver(Context context) {
mContext = context;
register(DEFAULT_ACTIONS);
}
public void register(List<String> 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;
}
}

View File

@ -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<String> 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"
));
}

View File

@ -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)

View File

@ -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));
}
}

View File

@ -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<M extends BaseModel> {
private final SQLiteDatabase mWritableSQLiteDatabase;
private final SQLiteDatabase mReadableSQLiteDatabase;
private final String mTable;
private final PublishSubject<ModelChange<M>> mModelChange = PublishSubject.create();
public Database(SQLiteOpenHelper sqLiteOpenHelper, String table) {
mWritableSQLiteDatabase = sqLiteOpenHelper.getWritableDatabase();
mReadableSQLiteDatabase = sqLiteOpenHelper.getWritableDatabase();
mTable = table;
}
public <T> Observable<T> exec(Callable<T> callable) {
return Observable.fromCallable(callable)
.subscribeOn(Schedulers.io());
}
public <T> Flowable<T> execFlowable(Callable<T> callable) {
return Flowable.fromCallable(callable)
.subscribeOn(Schedulers.io());
}
public PublishSubject<ModelChange<M>> getModelChange() {
return mModelChange;
}
public Observable<Integer> 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<Integer> 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<Long> 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<M> queryAllAsFlowable() {
return execFlowable(() ->
mReadableSQLiteDatabase.rawQuery("SELECT * FROM " + mTable, null)
)
.flatMap(cursor -> Flowable.fromIterable(() -> new CursorIterator(cursor)))
.map(this::createModelFromCursor);
}
public List<M> queryAll() {
ArrayList<M> 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<M> 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<Cursor> {
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;
}
}
}

View File

@ -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<IntentTask> {
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) {
}
}
}

View File

@ -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<M> {
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<M> {
return mData;
}
public BaseModel.Action getAction() {
public int getAction() {
return mAction;
}

View File

@ -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<TimedTask> {
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) {
}
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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<TimedTask> mTimedTaskModelAdapter;
private ModelAdapter<IntentTask> mIntentTaskModelAdapter;
private Context mContext;
private PublishSubject<ModelChange<TimedTask>> 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<TimedTask>() {
@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<TimedTask> getAllTasks() {
return RXSQLite.rx(SQLite.select().from(TimedTask.class))
.queryStreamResults()
.subscribeOn(Schedulers.io());
return mTimedTaskDatabase.queryAllAsFlowable();
}
public Flowable<IntentTask> 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<ModelChange<TimedTask>> 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<TimedTask> 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<IntentTask> getAllIntentTasksAsList() {
return mIntentTaskDatabase.queryAll();
}
public Observable<ModelChange<IntentTask>> getIntentTaskChanges() {
return mIntentTaskDatabase.getModelChange();
}
}

View File

@ -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;
}

View File

@ -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<ScriptFile> 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)

View File

@ -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;
}

View File

@ -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 {

View File

@ -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<Task> {
@Override
public void refresh() {
List<TimedTask> 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;
}
}

View File

@ -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<TimedTask> 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();

View File

@ -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<Integer, String> ACTIONS = BiMaps.<Integer, String>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<CheckBox> 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();
}
}

View File

@ -253,12 +253,86 @@
</com.github.aakira.expandablelayout.ExpandableRelativeLayout>
<RadioButton
android:id="@+id/run_on_boot_radio"
android:id="@+id/run_on_broadcast"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingLeft="16dp"
android:text="@string/text_run_on_boot"/>
android:text="@string/text_run_on_broadcast"/>
<com.github.aakira.expandablelayout.ExpandableRelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:ael_expanded="false"
app:ael_interpolator="fastOutSlowIn"
app:ael_orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="24dp"
android:orientation="vertical">
<RadioGroup
android:id="@+id/broadcast_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RadioButton
android:id="@+id/run_on_boot"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingLeft="8dp"
android:text="@string/text_run_on_boot"/>
<RadioButton
android:id="@+id/run_on_screen_on"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingLeft="8dp"
android:text="@string/text_run_on_screen_on"/>
<RadioButton
android:id="@+id/run_on_screen_off"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingLeft="8dp"
android:text="@string/text_run_on_screen_off"/>
<RadioButton
android:id="@+id/run_on_other_broadcast"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:paddingLeft="8dp"
android:text="@string/text_run_on_other_broadcast"/>
</RadioGroup>
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="36dp"
android:layout_marginTop="2dp">
<android.support.design.widget.TextInputEditText
android:id="@+id/action"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/text_broadcast_action"
android:text="@string/text_broadcast_action_prefix"/>
</android.support.design.widget.TextInputLayout>
</LinearLayout>
</com.github.aakira.expandablelayout.ExpandableRelativeLayout>
</RadioGroup>
</LinearLayout>

View File

@ -382,7 +382,7 @@
<string name="text_use_alarm_clock">使用系统闹钟唤醒Auto.js</string>
<string name="error_connect_to_remote">连接失败: %s</string>
<string name="text_are_you_sure_to_delete">确定要删除%s吗</string>
<string name="text_run_on_boot">开机时运行</string>
<string name="text_run_on_broadcast">特定事件(广播)触发运行</string>
<string name="text_search_java_class">搜索Java包/类</string>
<string name="text_class_or_package_name">类/包名</string>
<string name="text_view_docs">查看文档</string>
@ -403,4 +403,10 @@
<string name="foreground_notification_channel_name">前台服务通知</string>
<string name="foreground_notification_title">Auto.js保持运行中</string>
<string name="foreground_notification_text">点击进入主界面</string>
<string name="text_run_on_boot">开机时运行</string>
<string name="text_run_on_screen_on">亮屏时运行</string>
<string name="text_run_on_screen_off">息屏时运行</string>
<string name="text_run_on_other_broadcast">其他事件(广播)</string>
<string name="text_broadcast_action">广播Action</string>
<string name="text_broadcast_action_prefix">android.intent.action.</string>
</resources>

View File

@ -0,0 +1,11 @@
package com.stardust.util;
import java.util.Map;
import java.util.Set;
public interface BiMap<K, V> extends Map<K, V> {
K getKey(V value);
Set<V> valueSet();
}

View File

@ -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 <K, V> BiMap<K, V> make(Map<K, V> keyToValue, Map<V, K> valueToKey) {
return new BiMapImpl<>(keyToValue, valueToKey);
}
public static <K, V> BiMapBuilder<K, V> newBuilder() {
return new BiMapBuilder<>();
}
public static class BiMapBuilder<K, V> {
private final BiMap<K, V> mBiMap = make(new HashMap<K, V>(), new HashMap<V, K>());
public BiMapBuilder<K, V> put(K key, V value) {
mBiMap.put(key, value);
return this;
}
public BiMap<K, V> build() {
return mBiMap;
}
}
private static class BiMapImpl<K, V> implements BiMap<K, V> {
private final Map<K, V> mKVMap;
private final Map<V, K> mVKMap;
private BiMapImpl(Map<K, V> kvMap, Map<V, K> 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<? extends K, ? extends V> m) {
mKVMap.putAll(m);
}
@Override
public void clear() {
mKVMap.clear();
mVKMap.clear();
}
@NonNull
@Override
public Set<K> keySet() {
return mKVMap.keySet();
}
@Override
public K getKey(V value) {
return mVKMap.get(value);
}
@Override
public Set<V> valueSet() {
return mVKMap.keySet();
}
@NonNull
@Override
public Collection<V> values() {
return mKVMap.values();
}
@NonNull
@Override
public Set<Entry<K, V>> 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<? super K, ? super V> action) {
mKVMap.forEach(action);
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> 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<? super K, ? extends V> mappingFunction) {
return mKVMap.computeIfAbsent(key, mappingFunction);
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
return mKVMap.computeIfPresent(key, remappingFunction);
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
return mKVMap.compute(key, remappingFunction);
}
@RequiresApi(api = Build.VERSION_CODES.N)
@Override
public V merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
return mKVMap.merge(key, value, remappingFunction);
}
}
}