diff --git a/.gitignore b/.gitignore index 50688619..c2d30342 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ /captures .externalNativeBuild *.apk -*.exe \ No newline at end of file +*.exe +.idea/caches/build_file_checksums.ser diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 5257cc76..d6a56099 100644 Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ 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 41d8fa47..0b280778 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "org.autojs.autojs" minSdkVersion 17 targetSdkVersion 23 - versionCode 420 - versionName "4.0.3 Alpha" + versionCode 421 + versionName "4.0.3 Alpha2" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" multiDexEnabled true ndk { @@ -24,7 +24,7 @@ android { } release { shrinkResources false - minifyEnabled false + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } @@ -83,10 +83,10 @@ dependencies { }) annotationProcessor 'com.jakewharton:butterknife-compiler:8.6.0' // Android supports - compile 'com.android.support:appcompat-v7:25.4.0' - compile 'com.android.support:cardview-v7:25.4.0' - compile 'com.android.support:design:25.4.0' - compile 'com.android.support:multidex:1.0.2' + compile 'com.android.support:appcompat-v7:27.1.1' + compile 'com.android.support:cardview-v7:27.1.1' + compile 'com.android.support:design:27.1.1' + compile 'com.android.support:multidex:1.0.3' // Personal libraries compile 'com.github.hyb1996:MutableTheme:0.2.2' // Material Dialogs @@ -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 @@ -146,6 +141,10 @@ dependencies { compile('com.afollestad.material-dialogs:commons:0.9.2.3', { exclude group: 'com.android.support' }) + debugImplementation 'com.squareup.leakcanary:leakcanary-android:1.6.1' + releaseImplementation 'com.squareup.leakcanary:leakcanary-android-no-op:1.6.1' + // Optional, if you use support library fragments: + debugImplementation 'com.squareup.leakcanary:leakcanary-support-fragment:1.6.1' compile project(':automator') compile project(':common') compile project(':autojs') diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ef88f4bd..d68b0544 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -116,6 +116,7 @@ android:theme="@style/IssueReporterTheme"/> + @@ -132,10 +133,44 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/java/org/autojs/autojs/App.java b/app/src/main/java/org/autojs/autojs/App.java index d8f07b60..7738151e 100644 --- a/app/src/main/java/org/autojs/autojs/App.java +++ b/app/src/main/java/org/autojs/autojs/App.java @@ -1,5 +1,7 @@ package org.autojs.autojs; +import android.annotation.SuppressLint; +import android.content.Context; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.net.Uri; @@ -10,29 +12,26 @@ 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 org.autojs.autojs.autojs.AutoJs; -import org.autojs.autojs.autojs.key.GlobalKeyObserver; -import org.autojs.autojs.network.GlideApp; -import org.autojs.autojs.storage.database.IntentTaskDatabase; -import org.autojs.autojs.storage.database.TimedTaskDatabase; -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 org.autojs.autojs.autojs.AutoJs; +import org.autojs.autojs.autojs.key.GlobalKeyObserver; +import org.autojs.autojs.external.receiver.DynamicBroadcastReceivers; +import org.autojs.autojs.network.GlideApp; +import org.autojs.autojs.timing.IntentTask; +import org.autojs.autojs.timing.TimedTaskManager; +import org.autojs.autojs.timing.TimedTaskScheduler; +import org.autojs.autojs.tool.CrashHandler; +import org.autojs.autojs.ui.error.ErrorReportActivity; import java.lang.ref.WeakReference; +import java.util.ArrayList; /** * Created by Stardust on 2017/1/27. @@ -44,6 +43,7 @@ public class App extends MultiDexApplication { private static final String BUGLY_APP_ID = "19b3607b53"; private static WeakReference instance; + private DynamicBroadcastReceivers mDynamicBroadcastReceivers; public static App getApp() { return instance.get(); @@ -58,6 +58,10 @@ public class App extends MultiDexApplication { init(); } + public DynamicBroadcastReceivers getDynamicBroadcastReceivers() { + return mDynamicBroadcastReceivers; + } + private void setUpStaticsTool() { if (BuildConfig.DEBUG) return; @@ -67,6 +71,11 @@ public class App extends MultiDexApplication { } + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + } + private void setUpDebugEnvironment() { CrashHandler crashHandler = new CrashHandler(ErrorReportActivity.class); @@ -77,17 +86,16 @@ public class App extends MultiDexApplication { crashHandler.setBuglyHandler(Thread.getDefaultUncaughtExceptionHandler()); Thread.setDefaultUncaughtExceptionHandler(crashHandler); + if (LeakCanary.isInAnalyzerProcess(this)) { + // This process is dedicated to LeakCanary for heap analysis. + // You should not init your app in this process. + 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); @@ -96,6 +104,18 @@ public class App extends MultiDexApplication { } setupDrawableImageLoader(); TimedTaskScheduler.checkTasksRepeatedlyIfNeeded(this); + initDynamicBroadcastReceivers(); + } + + @SuppressLint("CheckResult") + private void initDynamicBroadcastReceivers() { + mDynamicBroadcastReceivers = new DynamicBroadcastReceivers(this); + TimedTaskManager.getInstance().getAllIntentTasks() + .filter(task -> task.getAction() != null) + .map(IntentTask::getAction) + .collectInto(new ArrayList(), ArrayList::add) + .subscribe(list -> mDynamicBroadcastReceivers.register(list), + Throwable::printStackTrace); } private void setupDrawableImageLoader() { diff --git a/app/src/main/java/org/autojs/autojs/Pref.java b/app/src/main/java/org/autojs/autojs/Pref.java index e63bc533..90a2c01f 100644 --- a/app/src/main/java/org/autojs/autojs/Pref.java +++ b/app/src/main/java/org/autojs/autojs/Pref.java @@ -201,4 +201,8 @@ public class Pref { getString(R.string.default_value_script_dir_path)); return new File(Environment.getExternalStorageDirectory(), dir).getPath(); } + + public static boolean isForegroundServiceEnabled() { + return def().getBoolean(getString(R.string.key_foreground_servie), false); + } } diff --git a/app/src/main/java/org/autojs/autojs/autojs/build/AutoJsApkBuilder.java b/app/src/main/java/org/autojs/autojs/autojs/build/AutoJsApkBuilder.java index 7c0b8dd3..419ce82c 100644 --- a/app/src/main/java/org/autojs/autojs/autojs/build/AutoJsApkBuilder.java +++ b/app/src/main/java/org/autojs/autojs/autojs/build/AutoJsApkBuilder.java @@ -50,6 +50,7 @@ public class AutoJsApkBuilder extends ApkBuilder { public static AppConfig fromProjectConfig(String projectDir, ProjectConfig projectConfig) { return new AppConfig() .setAppName(projectConfig.getName()) + .setPackageName(projectConfig.getPackageName()) .ignoreDir(new File(projectDir, projectConfig.getBuildDir())) .setVersionCode(projectConfig.getVersionCode()) .setVersionName(projectConfig.getVersionName()) diff --git a/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java b/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java new file mode 100644 index 00000000..e9108beb --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/external/foreground/ForegroundService.java @@ -0,0 +1,88 @@ +package org.autojs.autojs.external.foreground; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Build; +import android.os.IBinder; +import android.support.annotation.Nullable; +import android.support.annotation.RequiresApi; +import android.support.v4.app.NotificationCompat; + +import com.stardust.app.GlobalAppContext; + +import org.autojs.autojs.R; +import org.autojs.autojs.ui.main.MainActivity_; + +public class ForegroundService extends Service { + + + private static final int NOTIFICATION_ID = 1; + private static final String CHANEL_ID = ForegroundService.class.getName() + ".foreground"; + + public static void start(Context context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + context.startForegroundService(new Intent(context, ForegroundService.class)); + } else { + context.startService(new Intent(context, ForegroundService.class)); + } + } + + public static void stop(Context context){ + context.stopService(new Intent(context, ForegroundService.class)); + } + + @Override + public void onCreate() { + super.onCreate(); + startForeground(); + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } + + private void startForeground() { + startForeground(NOTIFICATION_ID, buildNotification()); + } + + private Notification buildNotification() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + createNotificationChannel(); + } + PendingIntent contentIntent = PendingIntent.getActivity(this, 0, MainActivity_.intent(this).get(), 0); + return new NotificationCompat.Builder(this, CHANEL_ID) + .setContentTitle(getString(R.string.foreground_notification_title)) + .setContentText(getString(R.string.foreground_notification_text)) + .setSmallIcon(R.drawable.autojs_material) + .setWhen(System.currentTimeMillis()) + .setContentIntent(contentIntent) + .setChannelId(CHANEL_ID) + .build(); + } + + @RequiresApi(api = Build.VERSION_CODES.O) + private void createNotificationChannel() { + NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + assert manager != null; + CharSequence name = getString(R.string.foreground_notification_channel_name); + String description = getString(R.string.foreground_notification_channel_name); + NotificationChannel channel = new NotificationChannel(CHANEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT); + channel.setDescription(description); + channel.enableLights(false); + manager.createNotificationChannel(channel); + } + + @Override + public void onDestroy() { + stopForeground(true); + super.onDestroy(); + + } +} 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..791fcbb4 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: intent = " + intent + ", this = " + this); + 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/DynamicBroadcastReceivers.java b/app/src/main/java/org/autojs/autojs/external/receiver/DynamicBroadcastReceivers.java new file mode 100644 index 00000000..b837f046 --- /dev/null +++ b/app/src/main/java/org/autojs/autojs/external/receiver/DynamicBroadcastReceivers.java @@ -0,0 +1,78 @@ +package org.autojs.autojs.external.receiver; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.IntentFilter; +import android.os.Build; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +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_PACKAGES_SUSPENDED; +import static android.content.Intent.ACTION_PACKAGES_UNSUSPENDED; +import static android.content.Intent.ACTION_SCREEN_OFF; +import static android.content.Intent.ACTION_SCREEN_ON; +import static android.content.Intent.ACTION_TIME_TICK; + +public class DynamicBroadcastReceivers { + + private static final List DEFAULT_ACTIONS = new ArrayList<>(Arrays.asList( + ACTION_TIME_TICK, + ACTION_SCREEN_OFF, + ACTION_SCREEN_ON, + 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 final Set mActions = new LinkedHashSet<>(); + private final List mReceivers = new ArrayList<>(); + private final Context mContext; + + + public DynamicBroadcastReceivers(Context context) { + mContext = context; + register(DEFAULT_ACTIONS); + } + + public void register(String action) { + register(Collections.singletonList(action)); + } + + public void register(List actions) { + IntentFilter filter = new IntentFilter(); + for (String action : actions) { + if (!StaticBroadcastReceiver.ACTIONS.contains(action) + && !mActions.contains(action)) { + mActions.add(action); + filter.addAction(action); + } + } + if (filter.countActions() == 0) { + return; + } + BaseBroadcastReceiver receiver = new BaseBroadcastReceiver(); + mContext.registerReceiver(receiver, filter); + } + + public void unregisterAll() { + for (BroadcastReceiver receiver : mReceivers) { + mContext.unregisterReceiver(receiver); + } + mReceivers.clear(); + } +} 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 0b5afb01..345d9ea8 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,8 @@ 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_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) @@ -63,4 +65,9 @@ public class TaskerScriptEditActivity extends BaseActivity { TaskerScriptEditActivity.super.finish(); } + @Override + protected void onDestroy() { + mEditorView.destroy(); + super.onDestroy(); + } } diff --git a/app/src/main/java/org/autojs/autojs/model/autocomplete/AutoCompletion.java b/app/src/main/java/org/autojs/autojs/model/autocomplete/AutoCompletion.java index 7bbbb675..ca95c75f 100644 --- a/app/src/main/java/org/autojs/autojs/model/autocomplete/AutoCompletion.java +++ b/app/src/main/java/org/autojs/autojs/model/autocomplete/AutoCompletion.java @@ -161,4 +161,7 @@ public class AutoCompletion { } + public void shutdown(){ + mExecutorService.shutdownNow(); + } } diff --git a/app/src/main/java/org/autojs/autojs/model/explorer/Explorer.java b/app/src/main/java/org/autojs/autojs/model/explorer/Explorer.java index ade9ea3f..cb8a5e39 100644 --- a/app/src/main/java/org/autojs/autojs/model/explorer/Explorer.java +++ b/app/src/main/java/org/autojs/autojs/model/explorer/Explorer.java @@ -30,6 +30,10 @@ public class Explorer { this(explorerProvider, cacheSize, EventBus.getDefault()); } + public ExplorerProvider getProvider() { + return mExplorerProvider; + } + public void notifyChildrenChanged(ExplorerPage page) { clearCache(page); mEventBus.post(new ExplorerChangeEvent(page, CHILDREN_CHANGE, null)); diff --git a/app/src/main/java/org/autojs/autojs/model/explorer/ExplorerSamleItem.java b/app/src/main/java/org/autojs/autojs/model/explorer/ExplorerSampleItem.java similarity index 58% rename from app/src/main/java/org/autojs/autojs/model/explorer/ExplorerSamleItem.java rename to app/src/main/java/org/autojs/autojs/model/explorer/ExplorerSampleItem.java index f469acd9..48170710 100644 --- a/app/src/main/java/org/autojs/autojs/model/explorer/ExplorerSamleItem.java +++ b/app/src/main/java/org/autojs/autojs/model/explorer/ExplorerSampleItem.java @@ -4,16 +4,16 @@ import com.stardust.pio.PFile; import java.io.File; -public class ExplorerSamleItem extends ExplorerFileItem { - public ExplorerSamleItem(PFile file, ExplorerPage parent) { +public class ExplorerSampleItem extends ExplorerFileItem { + public ExplorerSampleItem(PFile file, ExplorerPage parent) { super(file, parent); } - public ExplorerSamleItem(String path, ExplorerPage parent) { + public ExplorerSampleItem(String path, ExplorerPage parent) { super(path, parent); } - public ExplorerSamleItem(File file, ExplorerPage parent) { + public ExplorerSampleItem(File file, ExplorerPage parent) { super(file, parent); } diff --git a/app/src/main/java/org/autojs/autojs/model/explorer/WorkspaceFileProvider.java b/app/src/main/java/org/autojs/autojs/model/explorer/WorkspaceFileProvider.java index 403cad93..e9a147d2 100644 --- a/app/src/main/java/org/autojs/autojs/model/explorer/WorkspaceFileProvider.java +++ b/app/src/main/java/org/autojs/autojs/model/explorer/WorkspaceFileProvider.java @@ -8,6 +8,7 @@ import com.stardust.pio.PFile; import com.stardust.pio.PFiles; import org.autojs.autojs.Pref; +import org.autojs.autojs.model.sample.SampleFile; import java.io.File; import java.io.FileFilter; @@ -52,7 +53,7 @@ public class WorkspaceFileProvider extends ExplorerFileProvider { } } else { if (file.getPath().startsWith(mSampleDir.getPath())) { - p.addChild(new ExplorerSamleItem(file, p)); + p.addChild(new ExplorerSampleItem(file, p)); } else { p.addChild(new ExplorerFileItem(file, p)); } @@ -98,6 +99,10 @@ public class WorkspaceFileProvider extends ExplorerFileProvider { }); } + public static void resetSample(SampleFile sampleFile){ + + } + @Override protected ExplorerDirPage createExplorerPage(String path, ExplorerPage parent) { ExplorerDirPage page = super.createExplorerPage(path, parent); diff --git a/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginService.java b/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginService.java index 2824b3cb..51de8c25 100644 --- a/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginService.java +++ b/app/src/main/java/org/autojs/autojs/pluginclient/DevPluginService.java @@ -12,10 +12,9 @@ import android.util.Pair; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.stardust.util.MapEntries; +import com.stardust.util.MapBuilder; import org.autojs.autojs.BuildConfig; -import org.autojs.autojs.tool.EmptyObservers; import java.io.IOException; import java.net.SocketTimeoutException; @@ -24,7 +23,6 @@ import java.util.concurrent.TimeUnit; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.PublishSubject; import okhttp3.OkHttpClient; import okhttp3.Request; @@ -171,12 +169,12 @@ public class DevPluginService { @WorkerThread private void sayHelloToServer(JsonWebSocket socket) throws IOException { - writeMap(socket, TYPE_HELLO, new MapEntries() - .entry("device_name", Build.BRAND + " " + Build.MODEL) - .entry("client_version", CLIENT_VERSION) - .entry("app_version", BuildConfig.VERSION_NAME) - .entry("app_version_code", BuildConfig.VERSION_CODE) - .map()); + writeMap(socket, TYPE_HELLO, new MapBuilder() + .put("device_name", Build.BRAND + " " + Build.MODEL) + .put("client_version", CLIENT_VERSION) + .put("app_version", BuildConfig.VERSION_NAME) + .put("app_version_code", BuildConfig.VERSION_CODE) + .build()); mHandshakeTimeoutHandler.postDelayed(() -> { if (mSocket != socket && !socket.isClosed()) { onHandshakeTimeout(socket); 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..a8898ce9 100644 --- a/app/src/main/java/org/autojs/autojs/timing/TimedTaskManager.java +++ b/app/src/main/java/org/autojs/autojs/timing/TimedTaskManager.java @@ -1,29 +1,22 @@ 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 android.text.TextUtils; -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.App; +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; /** * Created by Stardust on 2017/11/27. @@ -31,12 +24,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 +36,132 @@ 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(i -> { + if(!TextUtils.isEmpty(intentTask.getAction())){ + App.getApp().getDynamicBroadcastReceivers() + .register(intentTask.getAction()); + } + }, 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); } + @SuppressLint("CheckResult") + public void updateTask(IntentTask task) { + mIntentTaskDatabase.update(task) + .subscribe(i -> { + if(i > 0 && !TextUtils.isEmpty(task.getAction())){ + App.getApp().getDynamicBroadcastReceivers() + .register(task.getAction()); + } + }, Throwable::printStackTrace); + } + 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(); + } + + public IntentTask getIntentTask(long intentTaskId) { + return mIntentTaskDatabase.queryById(intentTaskId); + } + + public Flowable getAllIntentTasks() { + return mIntentTaskDatabase.queryAllAsFlowable(); } } 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 535d4bbb..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; @@ -122,7 +123,11 @@ public class ScriptOperations { } private void notifyFileCreated(ScriptFile directory, ScriptFile scriptFile) { - mExplorer.notifyItemCreated(new ExplorerFileItem(scriptFile, mExplorerPage)); + if (scriptFile.isDirectory()) { + mExplorer.notifyItemCreated(new ExplorerDirPage(scriptFile, mExplorerPage)); + } else { + mExplorer.notifyItemCreated(new ExplorerFileItem(scriptFile, mExplorerPage)); + } } public void newFile() { @@ -315,9 +320,11 @@ 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) + .dir(Pref.getScriptDirPath()) .chooseDir() .singleChoice() .map(saveDir -> new File(saveDir, fileName).getPath()) diff --git a/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java b/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java index 3ddfbda7..86914a6c 100644 --- a/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/doc/DocsFragment.java @@ -74,23 +74,12 @@ public class DocsFragment extends ViewPagerFragment implements BackPressedHandle } - @Override - public void onResume() { - super.onResume(); - ((BackPressedHandler.HostActivity) getActivity()) - .getBackPressedObserver() - .registerHandlerAtFront(this); - } - @Override public void onPause() { super.onPause(); Bundle savedWebViewState = new Bundle(); mWebView.saveState(savedWebViewState); getArguments().putBundle("savedWebViewState", savedWebViewState); - ((BackPressedHandler.HostActivity) getActivity()) - .getBackPressedObserver() - .unregisterHandler(this); } @Override diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.java b/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.java index d293c100..0ec43fef 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/EditActivity.java @@ -180,6 +180,7 @@ EditActivity extends BaseActivity implements OnActivityResultDelegate.DelegateHo @Override protected void onDestroy() { + mEditorView.destroy(); super.onDestroy(); } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java b/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java index 3ba09bdb..3f9b827c 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/EditorView.java @@ -392,7 +392,7 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC Snackbar.make(this, R.string.text_start_running, Snackbar.LENGTH_SHORT).show(); } ScriptExecution execution = Scripts.runWithBroadcastSender(mFile); - if(execution == null){ + if (execution == null) { return null; } mScriptExecutionId = execution.getId(); @@ -671,4 +671,9 @@ public class EditorView extends FrameLayout implements CodeCompletionBar.OnHintC super.onRestoreInstanceState(superData); setMenuItemStatus(R.id.run, mScriptExecutionId == ScriptExecution.NO_ID); } + + public void destroy() { + mEditor.destroy(); + mAutoCompletion.shutdown(); + } } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java index 83b3db16..0c6eeceb 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/CodeEditor.java @@ -373,6 +373,11 @@ public class CodeEditor extends HVScrollView { mCodeEditText.removeAllBreakpoints(); } + public void destroy(){ + mJavaScriptHighlighter.shutdown(); + mJsBeautifier.shutdown(); + } + @Override protected void onDraw(Canvas canvas) { int codeWidth = getWidth() - getPaddingLeft() - getPaddingRight(); diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/JavaScriptHighlighter.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/JavaScriptHighlighter.java index 20752a55..7037a3ff 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/JavaScriptHighlighter.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/JavaScriptHighlighter.java @@ -2,10 +2,12 @@ package org.autojs.autojs.ui.edit.editor; import android.text.Editable; +import android.text.TextWatcher; import android.util.TimingLogger; import com.stardust.autojs.rhino.TokenStream; import com.stardust.pio.UncheckedIOException; + import org.autojs.autojs.ui.edit.theme.Theme; import org.autojs.autojs.ui.widget.SimpleTextWatcher; @@ -46,7 +48,7 @@ public class JavaScriptHighlighter implements SimpleTextWatcher.AfterTextChanged @Override public String toString() { - return super.toString() + "{count = " + mCount + ", length = " + mText.length() + "}"; + return super.toString() + "{count = " + mCount + ", length = " + mText.length() + "}"; } public int getCharCount() { @@ -60,16 +62,18 @@ public class JavaScriptHighlighter implements SimpleTextWatcher.AfterTextChanged private Theme mTheme; private CodeEditText mCodeEditText; - private ThreadPoolExecutor mExecutorService = new ThreadPoolExecutor(3, 6, + private ThreadPoolExecutor mExecutorService = new ThreadPoolExecutor(3, 6, 2L, TimeUnit.MINUTES, new LinkedBlockingQueue<>()); private AtomicInteger mRunningHighlighterId = new AtomicInteger(); private TimingLogger mLogger = new TimingLogger(CodeEditText.LOG_TAG, "highlight"); + private final TextWatcher mTextWatcher; public JavaScriptHighlighter(Theme theme, CodeEditText codeEditText) { mExecutorService.allowCoreThreadTimeOut(true); mTheme = theme; mCodeEditText = codeEditText; - codeEditText.addTextChangedListener(new SimpleTextWatcher(this)); + mTextWatcher = new SimpleTextWatcher(this); + codeEditText.addTextChangedListener(mTextWatcher); } @Override @@ -112,5 +116,9 @@ public class JavaScriptHighlighter implements SimpleTextWatcher.AfterTextChanged mCodeEditText.updateHighlightTokens(highlightTokens); } + public void shutdown() { + mCodeEditText.removeTextChangedListener(mTextWatcher); + mExecutorService.shutdownNow(); + } } diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewRedoUndoer.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewRedoUndoer.java deleted file mode 100644 index 06144eb0..00000000 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewRedoUndoer.java +++ /dev/null @@ -1,290 +0,0 @@ -package org.autojs.autojs.ui.edit.editor; -/* - * Copyright 2016. SHENQINCI(沈钦赐)<946736079@qq.com> - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -import android.support.annotation.NonNull; -import android.text.Editable; -import android.text.TextWatcher; -import android.widget.EditText; - -import java.util.Stack; - -/** - * 撤销和恢复撤销 - * Created by 沈钦赐 on 16/6/23. - */ -public class TextViewRedoUndoer { - //操作序号(一次编辑可能对应多个操作,如替换文字,就是删除+插入) - int index; - //撤销栈 - Stack history = new Stack<>(); - //恢复栈 - Stack historyBack = new Stack<>(); - - private Editable editable; - private EditText editText; - //自动操作标志,防止重复回调,导致无限撤销 - private boolean flag = false; - - private int mInitialHistoryStackSize; - private boolean mEnabled = true; - - public TextViewRedoUndoer(@NonNull EditText editText) { - this.editable = editText.getText(); - this.editText = editText; - editText.addTextChangedListener(new Watcher()); - } - - protected void onEditableChanged(Editable s) { - - } - - protected void onTextChanged(Editable s) { - if (history.size() < mInitialHistoryStackSize) { - mInitialHistoryStackSize = 0; - } - } - - - /** - * 清理记录 - * Clear history. - */ - public final void clearHistory() { - history.clear(); - historyBack.clear(); - mInitialHistoryStackSize = 0; - } - - public boolean canUndo() { - return !history.empty(); - } - - /** - * 撤销 - * Undo. - */ - public final void undo() { - if (history.empty()) return; - //锁定操作 - flag = true; - Action action = history.pop(); - historyBack.push(action); - if (action.isAdd) { - //撤销添加 - editable.delete(action.startCursor, action.startCursor + action.actionTarget.length()); - editText.setSelection(action.startCursor, action.startCursor); - } else { - //插销删除 - editable.insert(action.startCursor, action.actionTarget); - if (action.endCursor == action.startCursor) { - editText.setSelection(action.startCursor + action.actionTarget.length()); - } else { - editText.setSelection(action.startCursor, action.endCursor); - } - } - //释放操作 - flag = false; - //判断是否是下一个动作是否和本动作是同一个操作,直到不同为止 - if (!history.empty() && history.peek().index == action.index) { - undo(); - } - } - - public boolean canRedo() { - return !historyBack.empty(); - } - - /** - * 恢复 - * Redo. - */ - public final void redo() { - if (historyBack.empty()) return; - flag = true; - Action action = historyBack.pop(); - history.push(action); - if (action.isAdd) { - //恢复添加 - editable.insert(action.startCursor, action.actionTarget); - if (action.endCursor == action.startCursor) { - editText.setSelection(action.startCursor + action.actionTarget.length()); - } else { - editText.setSelection(action.startCursor, action.endCursor); - } - } else { - //恢复删除 - editable.delete(action.startCursor, action.startCursor + action.actionTarget.length()); - editText.setSelection(action.startCursor, action.startCursor); - } - flag = false; - //判断是否是下一个动作是否和本动作是同一个操作 - if (!historyBack.empty() && historyBack.peek().index == action.index) - redo(); - } - - /** - * 首次设置文本 - * Set default text. - */ - public final void setDefaultText(CharSequence text) { - clearHistory(); - flag = true; - editable.replace(0, editable.length(), text); - flag = false; - } - - public boolean isTextChanged() { - return history.size() != mInitialHistoryStackSize; - } - - public void markTextAsUnchanged() { - mInitialHistoryStackSize = history.size(); - } - - public void setEnabled(boolean enabled) { - mEnabled = enabled; - } - - public boolean isEnabled() { - return mEnabled; - } - - private class Watcher implements TextWatcher { - - /** - * Before text changed. - * - * @param s the s - * @param start the start 起始光标 - * @param count the endCursor 选择数量 - * @param after the after 替换增加的文字数 - */ - @Override - public final void beforeTextChanged(CharSequence s, int start, int count, int after) { - if (!editText.isEnabled() || !mEnabled) { - return; - } - if (flag) return; - int end = start + count; - if (end > start && end <= s.length()) { - CharSequence charSequence = s.subSequence(start, end); - //删除了文字 - if (charSequence.length() > 0) { - Action action = new Action(charSequence, start, false); - if (count > 1) { - //如果一次超过一个字符,说名用户选择了,然后替换或者删除操作 - action.setSelectCount(count); - } else if (count == 1 && count == after) { - //一个字符替换 - action.setSelectCount(count); - } - //还有一种情况:选择一个字符,然后删除(暂时没有考虑这种情况) - - history.push(action); - historyBack.clear(); - action.setIndex(++index); - } - } - } - - /** - * On text changed. - * - * @param s the s - * @param start the start 起始光标 - * @param before the before 选择数量 - * @param count the endCursor 添加的数量 - */ - @Override - public final void onTextChanged(CharSequence s, int start, int before, int count) { - if (!editText.isEnabled() || !mEnabled) { - return; - } - if (flag) return; - int end = start + count; - if (end > start) { - CharSequence charSequence = s.subSequence(start, end); - //添加文字 - if (charSequence.length() > 0) { - Action action = new Action(charSequence, start, true); - - history.push(action); - historyBack.clear(); - if (before > 0) { - //文字替换(先删除再增加),删除和增加是同一个操作,所以不需要增加序号 - action.setIndex(index); - } else { - action.setIndex(++index); - } - } - } - } - - @Override - public final void afterTextChanged(Editable s) { - if (!editText.isEnabled() || !mEnabled) { - return; - } - if (flag) return; - if (s != editable) { - editable = s; - onEditableChanged(s); - } - TextViewRedoUndoer.this.onTextChanged(s); - } - - } - - private class Action { - /** - * 改变字符. - */ - CharSequence actionTarget; - /** - * 光标位置. - */ - int startCursor; - int endCursor; - /** - * 标志增加操作. - */ - boolean isAdd; - /** - * 操作序号. - */ - int index; - - - public Action(CharSequence actionTag, int startCursor, boolean add) { - this.actionTarget = actionTag; - this.startCursor = startCursor; - this.endCursor = startCursor; - this.isAdd = add; - } - - public void setSelectCount(int count) { - this.endCursor = endCursor + count; - } - - public void setIndex(int index) { - this.index = index; - } - } - - -} \ No newline at end of file diff --git a/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewUndoRedo.java b/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewUndoRedo.java index 01b6a173..c9c54751 100644 --- a/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewUndoRedo.java +++ b/app/src/main/java/org/autojs/autojs/ui/edit/editor/TextViewUndoRedo.java @@ -9,6 +9,7 @@ import java.util.LinkedList; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; +import android.os.Handler; import android.text.Editable; import android.text.Selection; import android.text.TextWatcher; @@ -46,6 +47,10 @@ public class TextViewUndoRedo { private int mInitialHistoryStackSize; + private Handler mHandler = new Handler(); + private int mTextChangeId = 0; + private boolean mTextChanging = false; + // =================================================================== // /** @@ -404,7 +409,6 @@ public class TextViewUndoRedo { if (mIsUndoOrRedo || !mEnabled) { return; } - mBeforeChange = s.subSequence(start, start + count); } @@ -415,7 +419,10 @@ public class TextViewUndoRedo { } mAfterChange = s.subSequence(start, start + count); + mTextChangeId++; mEditHistory.add(new EditItem(start, mBeforeChange, mAfterChange)); + int textChangeId = mTextChangeId; + //TODO 增加连续输入文字当成一次撤销的功能 } public void afterTextChanged(Editable s) { diff --git a/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerProjectToolbar.java b/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerProjectToolbar.java index 1a1650e0..2f01565e 100644 --- a/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerProjectToolbar.java +++ b/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerProjectToolbar.java @@ -4,9 +4,6 @@ import android.content.Context; import android.support.annotation.Nullable; import android.support.v7.widget.CardView; import android.util.AttributeSet; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; @@ -14,7 +11,6 @@ import com.stardust.autojs.project.ProjectConfig; import com.stardust.autojs.project.ProjectLauncher; import com.stardust.pio.PFile; -import org.androidannotations.annotations.Click; import org.autojs.autojs.R; import org.autojs.autojs.autojs.AutoJs; import org.autojs.autojs.model.explorer.ExplorerChangeEvent; @@ -59,15 +55,19 @@ public class ExplorerProjectToolbar extends CardView { setOnClickListener(view -> edit()); } - public void setProject(PFile dir, ProjectConfig config) { + public void setProject(PFile dir) { + mProjectConfig = ProjectConfig.fromProjectDir(dir.getPath()); + if(mProjectConfig == null){ + setVisibility(GONE); + return; + } mDirectory = dir; - mProjectConfig = config; - mProjectName.setText(config.getName()); + mProjectName.setText(mProjectConfig.getName()); } public void refresh() { if (mDirectory != null) { - setProject(mDirectory, ProjectConfig.fromProjectDir(mDirectory.getPath())); + setProject(mDirectory); } } diff --git a/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.java b/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.java index f4956775..a91384ff 100644 --- a/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.java +++ b/app/src/main/java/org/autojs/autojs/ui/explorer/ExplorerView.java @@ -27,7 +27,11 @@ import org.autojs.autojs.model.explorer.ExplorerFileItem; import org.autojs.autojs.model.explorer.ExplorerItem; import org.autojs.autojs.model.explorer.ExplorerPage; import org.autojs.autojs.model.explorer.ExplorerProjectPage; +import org.autojs.autojs.model.explorer.ExplorerSampleItem; import org.autojs.autojs.model.explorer.ExplorerSamplePage; +import org.autojs.autojs.model.explorer.Explorers; +import org.autojs.autojs.model.explorer.WorkspaceFileProvider; +import org.autojs.autojs.model.sample.SampleFile; import org.autojs.autojs.model.script.ScriptFile; import org.autojs.autojs.model.script.Scripts; import org.autojs.autojs.ui.project.BuildActivity; @@ -111,7 +115,7 @@ public class ExplorerView extends ThemeColorSwipeRefreshLayout implements SwipeR mCurrentPageState = currentPageState; if (mCurrentPageState.page instanceof ExplorerProjectPage) { mProjectToolbar.setVisibility(VISIBLE); - mProjectToolbar.setProject(currentPageState.page.toScriptFile(), ((ExplorerProjectPage) currentPageState.page).getProjectConfig()); + mProjectToolbar.setProject(currentPageState.page.toScriptFile()); } else { mProjectToolbar.setVisibility(GONE); } @@ -306,6 +310,8 @@ public class ExplorerView extends ThemeColorSwipeRefreshLayout implements SwipeR case R.id.action_sort_by_size: sort(ExplorerItemList.SORT_TYPE_SIZE, mDirSortMenuShowing); break; + case R.id.reset: + // WorkspaceFileProvider.resetSample(mSelectedItem.toScriptFile()); default: return false; } @@ -487,6 +493,9 @@ public class ExplorerView extends ThemeColorSwipeRefreshLayout implements SwipeR } if (!mExplorerItem.canRename()) { menu.removeItem(R.id.rename); + } + if(mExplorerItem instanceof ExplorerSampleItem){ + } popupMenu.setOnMenuItemClickListener(ExplorerView.this); popupMenu.show(); 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/floating/CircularMenu.java b/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenu.java index aaa95359..ffc5e0de 100644 --- a/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenu.java +++ b/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenu.java @@ -115,7 +115,7 @@ public class CircularMenu implements Recorder.OnStateChangedListener, LayoutInsp @Override public View inflateActionView(FloatyService service, CircularMenuWindow window) { View actionView = View.inflate(service, R.layout.circular_action_view, null); - mActionViewIcon = (RoundedImageView) actionView.findViewById(R.id.icon); + mActionViewIcon = actionView.findViewById(R.id.icon); return actionView; } diff --git a/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenuWindow.java b/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenuWindow.java index ee0f170b..8f57a5e6 100644 --- a/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenuWindow.java +++ b/app/src/main/java/org/autojs/autojs/ui/floating/CircularMenuWindow.java @@ -1,11 +1,8 @@ package org.autojs.autojs.ui.floating; -import android.app.Activity; import android.content.Context; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.util.Log; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; import android.view.OrientationEventListener; import android.view.View; import android.view.WindowManager; @@ -15,9 +12,13 @@ import com.stardust.enhancedfloaty.FloatyWindow; import com.stardust.enhancedfloaty.WindowBridge; import com.stardust.floatingcircularactionmenu.CircularActionMenu; import com.stardust.floatingcircularactionmenu.gesture.BounceDragGesture; +import com.stardust.util.ScreenMetrics; public class CircularMenuWindow implements FloatyWindow { + private static final String KEY_POSITION_X = CircularMenuWindow.class.getName() + ".position.x"; + private static final String KEY_POSITION_Y = CircularMenuWindow.class.getName() + ".position.y"; + protected CircularMenuFloaty mFloaty; protected WindowManager mWindowManager; protected CircularActionMenu mCircularActionMenu; @@ -66,6 +67,9 @@ public class CircularMenuWindow implements FloatyWindow { } private void setInitialState() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); + int y = preferences.getInt(KEY_POSITION_Y, ScreenMetrics.getDeviceScreenHeight() / 2); + mActionViewWindowBridge.updatePosition(mActionViewWindowBridge.getX(), y); keepToSide(); } @@ -190,6 +194,11 @@ public class CircularMenuWindow implements FloatyWindow { } public void close() { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(mContext); + preferences.edit() + .putInt(KEY_POSITION_X, mActionViewWindowBridge.getX()) + .putInt(KEY_POSITION_Y, mActionViewWindowBridge.getY()) + .apply(); mOrientationEventListener.disable(); this.mWindowManager.removeView(this.mCircularActionMenu); this.mWindowManager.removeView(this.mCircularActionView); diff --git a/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.java b/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.java index 026b77d2..c8d716dd 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/MainActivity.java @@ -37,6 +37,7 @@ import org.autojs.autojs.BuildConfig; import org.autojs.autojs.Pref; import org.autojs.autojs.R; import org.autojs.autojs.autojs.AutoJs; +import org.autojs.autojs.external.foreground.ForegroundService; import org.autojs.autojs.model.explorer.Explorers; import org.autojs.autojs.tool.AccessibilityServiceTool; import org.autojs.autojs.ui.BaseActivity; @@ -208,6 +209,7 @@ public class MainActivity extends BaseActivity implements OnActivityResultDelega public void exitCompletely() { finish(); FloatyWindowManger.hideCircularMenu(); + ForegroundService.stop(this); stopService(new Intent(this, FloatyService.class)); AutoJs.getInstance().getScriptEngineService().stopAll(); } @@ -264,6 +266,12 @@ public class MainActivity extends BaseActivity implements OnActivityResultDelega @Override public void onBackPressed() { + Fragment fragment = mPagerAdapter.getStoredFragment(mViewPager.getCurrentItem()); + if (fragment instanceof BackPressedHandler) { + if (((BackPressedHandler) fragment).onBackPressed(this)) { + return; + } + } if (!mBackPressObserver.onBackPressed(this)) { super.onBackPressed(); } diff --git a/app/src/main/java/org/autojs/autojs/ui/main/ViewPagerFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/ViewPagerFragment.java index 4649dc02..068786c6 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/ViewPagerFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/ViewPagerFragment.java @@ -3,14 +3,17 @@ package org.autojs.autojs.ui.main; import android.support.annotation.CallSuper; import android.support.design.widget.FloatingActionButton; import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; import android.support.v4.view.animation.FastOutSlowInInterpolator; import android.view.View; +import com.stardust.util.BackPressedHandler; + /** * Created by Stardust on 2017/8/22. */ -public abstract class ViewPagerFragment extends Fragment { +public abstract class ViewPagerFragment extends Fragment implements BackPressedHandler { protected static final int ROTATION_GONE = -1; diff --git a/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityFragment.java index 203180e1..e9f0b637 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/community/CommunityFragment.java @@ -72,23 +72,12 @@ public class CommunityFragment extends ViewPagerFragment implements BackPressedH } } - @Override - public void onResume() { - super.onResume(); - ((BackPressedHandler.HostActivity) getActivity()) - .getBackPressedObserver() - .registerHandlerAtFront(this); - } - @Override public void onPause() { super.onPause(); Bundle savedWebViewState = new Bundle(); mWebView.saveState(savedWebViewState); getArguments().putBundle("savedWebViewState", savedWebViewState); - ((BackPressedHandler.HostActivity) getActivity()) - .getBackPressedObserver() - .unregisterHandler(this); } @Override diff --git a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java index 5d7ccae0..09643151 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/drawer/DrawerFragment.java @@ -1,5 +1,6 @@ package org.autojs.autojs.ui.main.drawer; +import android.annotation.SuppressLint; import android.content.Intent; import android.graphics.drawable.Drawable; import android.os.Build; @@ -22,6 +23,7 @@ import com.stardust.notification.NotificationListenerService; import org.autojs.autojs.Pref; import org.autojs.autojs.R; +import org.autojs.autojs.external.foreground.ForegroundService; import org.autojs.autojs.network.GlideApp; import org.autojs.autojs.network.UserService; import org.autojs.autojs.tool.EmptyObservers; @@ -107,6 +109,8 @@ public class DrawerFragment extends android.support.v4.app.Fragment { }; private DrawerMenuItem mNotificationPermissionItem = new DrawerMenuItem(R.drawable.ic_ali_notification, R.string.text_notification_permission, 0, this::goToNotificationServiceSettings); + private DrawerMenuItem mForegroundServiceItem = new DrawerMenuItem(R.drawable.ic_service_green, R.string.text_foreground_service, R.string.key_foreground_servie, this::toggleForegroundService); + private DrawerMenuItem mFloatingWindowItem = new DrawerMenuItem(R.drawable.ic_robot_64, R.string.text_floating_window, 0, this::showOrDismissFloatingWindow); private DrawerMenuItem mCheckForUpdatesItem = new DrawerMenuItem(R.drawable.ic_check_for_updates, R.string.text_check_for_updates, this::checkForUpdates); @@ -142,6 +146,10 @@ public class DrawerFragment extends android.support.v4.app.Fragment { setChecked(mFloatingWindowItem, true); } setChecked(mConnectionItem, DevPluginService.getInstance().isConnected()); + if(Pref.isForegroundServiceEnabled()){ + ForegroundService.start(GlobalAppContext.get()); + setChecked(mForegroundServiceItem, true); + } } private void initMenuItems() { @@ -150,6 +158,7 @@ public class DrawerFragment extends android.support.v4.app.Fragment { mAccessibilityServiceItem, mStableModeItem, mNotificationPermissionItem, + mForegroundServiceItem, new DrawerMenuGroup(R.string.text_script_record), mFloatingWindowItem, @@ -165,6 +174,7 @@ public class DrawerFragment extends android.support.v4.app.Fragment { } + @SuppressLint("CheckResult") @Click(R.id.avatar) void loginOrShowUserInfo() { UserService.getInstance() @@ -251,6 +261,17 @@ public class DrawerFragment extends android.support.v4.app.Fragment { } } + + private void toggleForegroundService(DrawerMenuItemViewHolder holder) { + boolean checked = holder.getSwitchCompat().isChecked(); + if(checked){ + ForegroundService.start(GlobalAppContext.get()); + }else { + ForegroundService.stop(GlobalAppContext.get()); + } + } + + private void inputRemoteHost() { String host = Pref.getServerAddressOrDefault(WifiTool.getRouterIp(getActivity())); new MaterialDialog.Builder(getActivity()) diff --git a/app/src/main/java/org/autojs/autojs/ui/main/sample/MarketFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/sample/MarketFragment.java index ede9cc57..5269ad05 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/sample/MarketFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/sample/MarketFragment.java @@ -62,7 +62,6 @@ public class MarketFragment extends ViewPagerFragment implements BackPressedHand @Override public boolean onBackPressed(Activity activity) { - return false; } diff --git a/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java index 23e04bf4..a8fd7241 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/scripts/MyScriptListFragment.java @@ -5,7 +5,9 @@ import android.os.Bundle; import android.preference.PreferenceManager; import android.support.annotation.Nullable; import android.support.design.widget.FloatingActionButton; +import android.support.v7.widget.RecyclerView; +import com.stardust.app.GlobalAppContext; import com.stardust.util.BackPressedHandler; import com.stardust.util.IntentUtil; @@ -26,6 +28,7 @@ import org.autojs.autojs.ui.main.ViewPagerFragment; import org.autojs.autojs.ui.project.ProjectConfigActivity; import org.autojs.autojs.ui.project.ProjectConfigActivity_; import org.autojs.autojs.ui.viewmodel.ExplorerItemList; +import org.autojs.autojs.ui.widget.ExpandableRecyclerView; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -35,7 +38,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers; * Created by Stardust on 2017/3/13. */ @EFragment(R.layout.fragment_my_script_list) -public class MyScriptListFragment extends ViewPagerFragment implements BackPressedHandler, FloatingActionMenu.OnFloatingActionButtonClickListener { +public class MyScriptListFragment extends ViewPagerFragment implements FloatingActionMenu.OnFloatingActionButtonClickListener { private static final String TAG = "MyScriptListFragment"; @@ -63,27 +66,11 @@ public class MyScriptListFragment extends ViewPagerFragment implements BackPress if (item.isEditable()) { Scripts.edit(item.toScriptFile()); } else { - IntentUtil.viewFile(getContext(), item.getPath()); + IntentUtil.viewFile(GlobalAppContext.get(), item.getPath()); } }); } - @Override - public void onResume() { - super.onResume(); - ((BackPressedHandler.HostActivity) getActivity()) - .getBackPressedObserver() - .registerHandlerAtFront(this); - } - - @Override - public void onPause() { - super.onPause(); - ((BackPressedHandler.HostActivity) getActivity()) - .getBackPressedObserver() - .unregisterHandler(this); - } - @Override protected void onFabClick(FloatingActionButton fab) { initFloatingActionMenuIfNeeded(fab); @@ -98,7 +85,7 @@ public class MyScriptListFragment extends ViewPagerFragment implements BackPress private void initFloatingActionMenuIfNeeded(final FloatingActionButton fab) { if (mFloatingActionMenu != null) return; - mFloatingActionMenu = ((FloatingActionMenu) getActivity().findViewById(R.id.floating_action_menu)); + mFloatingActionMenu = getActivity().findViewById(R.id.floating_action_menu); mFloatingActionMenu.getState() .observeOn(AndroidSchedulers.mainThread()) .subscribe(new SimpleObserver() { 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..3ad1ec0a 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 @@ -1,24 +1,33 @@ package org.autojs.autojs.ui.main.task; +import android.content.Intent; + import com.stardust.app.GlobalAppContext; import com.stardust.autojs.engine.ScriptEngine; import com.stardust.autojs.execution.ScriptExecution; import com.stardust.autojs.script.AutoFileSource; import com.stardust.autojs.script.JavaScriptSource; import com.stardust.pio.PFiles; +import com.stardust.util.MapBuilder; import org.autojs.autojs.R; +import org.autojs.autojs.timing.IntentTask; import org.autojs.autojs.timing.TimedTask; import org.autojs.autojs.timing.TimedTaskManager; import org.joda.time.format.DateTimeFormat; +import java.util.Map; + +import static org.autojs.autojs.ui.timing.TimedTaskSettingActivity.ACTION_DESC_MAP; + /** * Created by Stardust on 2017/11/28. */ public abstract class Task { + public abstract String getName(); public abstract String getDesc(); @@ -29,11 +38,26 @@ 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,24 +66,47 @@ 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; + Integer desc = ACTION_DESC_MAP.get(mIntentTask.getAction()); + if(desc != null){ + return GlobalAppContext.getString(desc); + } + return mIntentTask.getAction(); + } + } @Override public void cancel() { - TimedTaskManager.getInstance().removeTask(mTimedTask); + if (mTimedTask != null) { + TimedTaskManager.getInstance().removeTask(mTimedTask); + } else { + TimedTaskManager.getInstance().removeTask(mIntentTask); + } + } + + 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 +116,16 @@ public abstract class Task { public void setTimedTask(TimedTask timedTask) { mTimedTask = timedTask; } + + public void setIntentTask(IntentTask intentTask) { + mIntentTask = intentTask; + } + + public long getId() { + if(mTimedTask != null) + return mTimedTask.getId(); + return mIntentTask.getId(); + } } 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..18a6f018 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,56 @@ 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..3371aa46 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,28 @@ 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 org.autojs.autojs.ui.timing.TimedTaskSettingActivity_; -import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; - import java.util.ArrayList; import java.util.List; @@ -60,7 +58,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 +133,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 +152,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(); } } @@ -249,8 +249,11 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView { void onItemClick(View view) { if (mTask instanceof Task.PendingTask) { + Task.PendingTask task = (Task.PendingTask) mTask; + String extra = task.getTimedTask() == null ? TimedTaskSettingActivity.EXTRA_INTENT_TASK_ID + : TimedTaskSettingActivity.EXTRA_TASK_ID; TimedTaskSettingActivity_.intent(getContext()) - .extra(TaskReceiver.EXTRA_TASK_ID, ((Task.PendingTask) mTask).getTimedTask().getId()) + .extra(extra, task.getId()) .start(); } } @@ -263,8 +266,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/main/task/TaskManagerFragment.java b/app/src/main/java/org/autojs/autojs/ui/main/task/TaskManagerFragment.java index e0e8f293..d6ffb284 100644 --- a/app/src/main/java/org/autojs/autojs/ui/main/task/TaskManagerFragment.java +++ b/app/src/main/java/org/autojs/autojs/ui/main/task/TaskManagerFragment.java @@ -1,5 +1,6 @@ package org.autojs.autojs.ui.main.task; +import android.app.Activity; import android.os.Bundle; import android.support.design.widget.FloatingActionButton; import android.support.v4.widget.SwipeRefreshLayout; @@ -70,4 +71,8 @@ public class TaskManagerFragment extends ViewPagerFragment { AutoJs.getInstance().getScriptEngineService().stopAll(); } + @Override + public boolean onBackPressed(Activity activity) { + return false; + } } diff --git a/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java b/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java index e338bd49..2eadd7f1 100644 --- a/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/project/BuildActivity.java @@ -39,6 +39,7 @@ import org.autojs.autojs.ui.shortcut.ShortcutIconSelectActivity_; import java.io.File; import java.io.InputStream; +import java.security.KeyStore; import java.util.Locale; import java.util.concurrent.Callable; @@ -116,7 +117,6 @@ public class BuildActivity extends BaseActivity implements AutoJsApkBuilder.Prog if (version < ApkBuilderPluginHelper.getSuitablePluginVersion()) { showPluginDownloadDialog(R.string.apk_builder_plugin_version_too_low, false); } - } private void showPluginDownloadDialog(int msgRes, boolean finishIfCanceled) { diff --git a/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.java b/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.java index 6351663b..3950d0f6 100644 --- a/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/project/ProjectConfigActivity.java @@ -12,6 +12,7 @@ import android.widget.ImageView; import android.widget.Toast; import com.stardust.autojs.project.ProjectConfig; +import com.stardust.autojs.runtime.api.Dialogs; import com.stardust.pio.PFiles; import org.androidannotations.annotations.AfterViews; @@ -24,6 +25,7 @@ import org.autojs.autojs.model.explorer.ExplorerFileItem; import org.autojs.autojs.model.explorer.ExplorerItem; import org.autojs.autojs.model.explorer.Explorers; import org.autojs.autojs.model.project.ProjectTemplate; +import org.autojs.autojs.theme.dialog.ThemeColorMaterialDialogBuilder; import org.autojs.autojs.ui.BaseActivity; import org.autojs.autojs.ui.widget.SimpleTextWatcher; @@ -89,11 +91,21 @@ public class ProjectConfigActivity extends BaseActivity { } mDirectory = new File(dir); mProjectConfig = ProjectConfig.fromProjectDir(dir); + if (mProjectConfig == null) { + new ThemeColorMaterialDialogBuilder(this) + .title(R.string.text_invalid_project) + .positiveText(R.string.ok) + .dismissListener(dialogInterface -> finish()) + .show(); + } } } @AfterViews void setupViews() { + if (mProjectConfig == null) { + return; + } setToolbarAsBack(mNewProject ? getString(R.string.text_new_project) : mProjectConfig.getName()); if (mNewProject) { mAppName.addTextChangedListener(new SimpleTextWatcher(s -> diff --git a/app/src/main/java/org/autojs/autojs/ui/settings/SettingsActivity.java b/app/src/main/java/org/autojs/autojs/ui/settings/SettingsActivity.java index bcf4ccbc..a8423d6d 100644 --- a/app/src/main/java/org/autojs/autojs/ui/settings/SettingsActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/settings/SettingsActivity.java @@ -12,7 +12,7 @@ import android.view.View; import com.stardust.theme.app.ColorSelectActivity; import com.stardust.theme.preference.ThemeColorPreferenceFragment; import com.stardust.theme.util.ListBuilder; -import com.stardust.util.MapEntries; +import com.stardust.util.MapBuilder; import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.EActivity; @@ -100,14 +100,14 @@ public class SettingsActivity extends BaseActivity { @Override public void onStart() { super.onStart(); - ACTION_MAP = new MapEntries() - .entry(getString(R.string.text_theme_color), () -> selectThemeColor(getActivity())) - .entry(getString(R.string.text_check_for_updates), () -> new UpdateCheckDialog(getActivity()) + ACTION_MAP = new MapBuilder() + .put(getString(R.string.text_theme_color), () -> selectThemeColor(getActivity())) + .put(getString(R.string.text_check_for_updates), () -> new UpdateCheckDialog(getActivity()) .show()) - .entry(getString(R.string.text_issue_report), () -> startActivity(new Intent(getActivity(), IssueReporterActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))) - .entry(getString(R.string.text_about_me_and_repo), () -> startActivity(new Intent(getActivity(), AboutActivity_.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))) - .entry(getString(R.string.text_licenses), () -> showLicenseDialog()) - .map(); + .put(getString(R.string.text_issue_report), () -> startActivity(new Intent(getActivity(), IssueReporterActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))) + .put(getString(R.string.text_about_me_and_repo), () -> startActivity(new Intent(getActivity(), AboutActivity_.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))) + .put(getString(R.string.text_licenses), () -> showLicenseDialog()) + .build(); } @Override diff --git a/app/src/main/java/org/autojs/autojs/ui/splash/SplashActivity.java b/app/src/main/java/org/autojs/autojs/ui/splash/SplashActivity.java index fc73ba14..ec5f693a 100644 --- a/app/src/main/java/org/autojs/autojs/ui/splash/SplashActivity.java +++ b/app/src/main/java/org/autojs/autojs/ui/splash/SplashActivity.java @@ -51,7 +51,7 @@ public class SplashActivity extends BaseActivity { mNotStartMainActivity = getIntent().getBooleanExtra(NOT_START_MAIN_ACTIVITY, false); boolean forceShowAd = getIntent().getBooleanExtra(FORCE_SHOW_AD, false); setContentView(R.layout.activity_splash); - mFullScreenAdView = (FullScreenAdView) findViewById(R.id.full_screen_view); + mFullScreenAdView = findViewById(R.id.full_screen_view); if (!forceShowAd && !Pref.shouldShowAd()) { mFullScreenAdView.setVisibility(View.INVISIBLE); mHandler.postDelayed(this::enterNextActivity, 1500); 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..a4d4ce31 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 @@ -3,6 +3,8 @@ package org.autojs.autojs.ui.timing; import android.app.DatePickerDialog; import android.app.TimePickerDialog; import android.content.Intent; +import android.graphics.ColorFilter; +import android.net.ConnectivityManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -18,6 +20,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 +30,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.MapBuilder; import org.androidannotations.annotations.AfterViews; import org.androidannotations.annotations.CheckedChange; @@ -49,6 +55,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. @@ -56,12 +63,51 @@ import java.util.List; @EActivity(R.layout.activity_timed_task_setting) public class TimedTaskSettingActivity extends BaseActivity { + public static final String EXTRA_INTENT_TASK_ID = "intent_task_id"; + public static final String EXTRA_TASK_ID = TaskReceiver.EXTRA_TASK_ID; private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormat.forPattern("HH:mm"); private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormat.forPattern("yy-MM-dd"); private static final int REQUEST_CODE_IGNORE_BATTERY = 27101; + private static final String LOG_TAG = "TimedTaskSettings"; + + public static final Map ACTION_DESC_MAP = new MapBuilder() + .put(Intent.ACTION_BOOT_COMPLETED, R.string.text_run_on_boot) + .put(Intent.ACTION_SCREEN_OFF, R.string.text_run_on_screen_off) + .put(Intent.ACTION_SCREEN_ON, R.string.text_run_on_screen_on) + .put(Intent.ACTION_USER_PRESENT, R.string.text_run_on_screen_unlock) + .put(Intent.ACTION_BATTERY_CHANGED, R.string.text_run_on_battery_change) + .put(Intent.ACTION_POWER_CONNECTED, R.string.text_run_on_power_connect) + .put(Intent.ACTION_POWER_DISCONNECTED, R.string.text_run_on_power_disconnect) + .put(ConnectivityManager.CONNECTIVITY_ACTION, R.string.text_run_on_conn_change) + .put(Intent.ACTION_PACKAGE_ADDED, R.string.text_run_on_package_install) + .put(Intent.ACTION_PACKAGE_REMOVED, R.string.text_run_on_package_uninstall) + .put(Intent.ACTION_PACKAGE_REPLACED, R.string.text_run_on_package_update) + .put(Intent.ACTION_HEADSET_PLUG, R.string.text_run_on_headset_plug) + .put(Intent.ACTION_CONFIGURATION_CHANGED, R.string.text_run_on_config_change) + .put(Intent.ACTION_TIME_TICK, R.string.text_run_on_time_tick) + .build(); + + 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) + .put(R.id.run_on_screen_unlock, Intent.ACTION_USER_PRESENT) + .put(R.id.run_on_battery_change, Intent.ACTION_BATTERY_CHANGED) + .put(R.id.run_on_power_connect, Intent.ACTION_POWER_CONNECTED) + .put(R.id.run_on_power_disconnect, Intent.ACTION_POWER_DISCONNECTED) + .put(R.id.run_on_conn_change, ConnectivityManager.CONNECTIVITY_ACTION) + .put(R.id.run_on_package_install, Intent.ACTION_PACKAGE_ADDED) + .put(R.id.run_on_package_uninstall, Intent.ACTION_PACKAGE_REMOVED) + .put(R.id.run_on_package_update, Intent.ACTION_PACKAGE_REPLACED) + .put(R.id.run_on_headset_plug, Intent.ACTION_HEADSET_PLUG) + .put(R.id.run_on_config_change, Intent.ACTION_CONFIGURATION_CHANGED) + .put(R.id.run_on_time_tick, Intent.ACTION_TIME_TICK) + .build(); + + @ViewById(R.id.toolbar) Toolbar mToolbar; @@ -77,9 +123,17 @@ 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 mRunOnBroadcastRadio; + @ViewById(R.id.run_on_other_broadcast) + RadioButton mRunOnOtherBroadcast; + + @ViewById(R.id.action) + EditText mOtherBroadcastAction; + + @ViewById(R.id.broadcast_group) + RadioGroup mBroadcastGroup; @ViewById(R.id.disposable_task_time) TextView mDisposableTaskTime; @@ -98,25 +152,33 @@ public class TimedTaskSettingActivity extends BaseActivity { private List mDayOfWeekCheckBoxes = new ArrayList<>(); - private ScriptFile mScriptFile; private TimedTask mTimedTask; + private IntentTask mIntentTask; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - int taskId = getIntent().getIntExtra(TaskReceiver.EXTRA_TASK_ID, -1); + long taskId = getIntent().getLongExtra(EXTRA_TASK_ID, -1); if (taskId != -1) { mTimedTask = TimedTaskManager.getInstance().getTimedTask(taskId); if (mTimedTask != null) { mScriptFile = new ScriptFile(mTimedTask.getScriptPath()); } } else { - String path = getIntent().getStringExtra(ScriptIntents.EXTRA_KEY_PATH); - if (TextUtils.isEmpty(path)) { - finish(); + long intentTaskId = getIntent().getLongExtra(EXTRA_INTENT_TASK_ID, -1); + if (intentTaskId != -1) { + mIntentTask = TimedTaskManager.getInstance().getIntentTask(intentTaskId); + if (mIntentTask != null) { + mScriptFile = new ScriptFile(mIntentTask.getScriptPath()); + } + } else { + String path = getIntent().getStringExtra(ScriptIntents.EXTRA_KEY_PATH); + if (TextUtils.isEmpty(path)) { + finish(); + } + mScriptFile = new ScriptFile(path); } - mScriptFile = new ScriptFile(path); } } @@ -128,7 +190,7 @@ public class TimedTaskSettingActivity extends BaseActivity { mToolbar.setSubtitle(mScriptFile.getName()); } findDayOfWeekCheckBoxes(mWeeklyTaskContainer); - setUpTime(); + setUpTaskSettings(); } private void findDayOfWeekCheckBoxes(ViewGroup parent) { @@ -145,13 +207,32 @@ public class TimedTaskSettingActivity extends BaseActivity { } - private void setUpTime() { + private void setUpTaskSettings() { mDisposableTaskDate.setText(DATE_FORMATTER.print(LocalDate.now())); mDisposableTaskTime.setText(TIME_FORMATTER.print(LocalTime.now())); - if (mTimedTask == null) { - mDailyTaskRadio.setChecked(true); + if (mTimedTask != null) { + setupTime(); return; } + if (mIntentTask != null) { + setupAction(); + return; + } + mDailyTaskRadio.setChecked(true); + } + + private void setupAction() { + mRunOnBroadcastRadio.setChecked(true); + Integer buttonId = ACTIONS.getKey(mIntentTask.getAction()); + if (buttonId == null) { + mRunOnOtherBroadcast.setChecked(true); + mOtherBroadcastAction.setText(mIntentTask.getAction()); + } else { + ((RadioButton) findViewById(buttonId)).setChecked(true); + } + } + + private void setupTime() { if (mTimedTask.isDisposable()) { mDisposableTaskRadio.setChecked(true); mDisposableTaskTime.setText(TIME_FORMATTER.print(mTimedTask.getMillis())); @@ -171,11 +252,10 @@ public class TimedTaskSettingActivity extends BaseActivity { mDayOfWeekCheckBoxes.get(i).setChecked(mTimedTask.hasDayOfWeek(i + 1)); } } - } - @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()) { @@ -269,10 +349,8 @@ public class TimedTaskSettingActivity extends BaseActivity { startActivityForResult(new Intent().setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) .setData(Uri.parse("package:" + getPackageName())), REQUEST_CODE_IGNORE_BATTERY); } else { - createOrUpdateTimedTask(); + createOrUpdateTask(); } - - return true; } return super.onOptionsItemSelected(item); @@ -282,18 +360,14 @@ public class TimedTaskSettingActivity extends BaseActivity { protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_CODE_IGNORE_BATTERY) { Log.d(LOG_TAG, "result code = " + requestCode); - createOrUpdateTimedTask(); + createOrUpdateTask(); } super.onActivityResult(requestCode, resultCode, data); } - 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(); + private void createOrUpdateTask() { + if (mRunOnBroadcastRadio.isChecked()) { + createOrUpdateIntentTask(); return; } TimedTask task = createTimedTask(); @@ -301,6 +375,9 @@ public class TimedTaskSettingActivity extends BaseActivity { return; if (mTimedTask == null) { TimedTaskManager.getInstance().addTask(task); + if (mIntentTask != null) { + TimedTaskManager.getInstance().removeTask(mIntentTask); + } Toast.makeText(this, R.string.text_already_create, Toast.LENGTH_SHORT).show(); } else { task.setId(mTimedTask.getId()); @@ -308,4 +385,38 @@ public class TimedTaskSettingActivity extends BaseActivity { } finish(); } + + + private void createOrUpdateIntentTask() { + int buttonId = mBroadcastGroup.getCheckedRadioButtonId(); + if (buttonId == -1) { + Toast.makeText(this, R.string.error_empty_selection, Toast.LENGTH_SHORT).show(); + return; + } + 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()); + if (mIntentTask != null) { + task.setId(mIntentTask.getId()); + TimedTaskManager.getInstance().updateTask(task); + Toast.makeText(this, R.string.text_already_create, Toast.LENGTH_SHORT).show(); + } else { + TimedTaskManager.getInstance().addTask(task); + if (mTimedTask != null) { + TimedTaskManager.getInstance().removeTask(mTimedTask); + } + } + + finish(); + } } diff --git a/app/src/main/java/org/autojs/autojs/ui/widget/BubblePopupMenu.java b/app/src/main/java/org/autojs/autojs/ui/widget/BubblePopupMenu.java index a65d392e..74c5f448 100644 --- a/app/src/main/java/org/autojs/autojs/ui/widget/BubblePopupMenu.java +++ b/app/src/main/java/org/autojs/autojs/ui/widget/BubblePopupMenu.java @@ -58,7 +58,7 @@ public class BubblePopupMenu extends PopupWindow { } else { params.leftMargin = 0; } - if (y + height > screenHeight) { + if (y > screenHeight / 2) { getContentView().setRotation(180); mRecyclerView.setRotation(180); params.leftMargin = -params.leftMargin; 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..fc19525c 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,175 @@ + 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 b06d2b6a..ec195a39 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包/类 类/包名 查看文档 @@ -397,4 +397,28 @@ 项目位置 新建项目 js文件 + 无效项目 + 前台服务 + key_foreground_service + 前台服务通知 + Auto.js保持运行中 + 点击进入主界面 + 其他事件(广播) + 广播Action + android.intent.action. + 开机时 + 亮屏时 + 息屏时 + 请选择一个广播事件选项 + 电量变化时 + 屏幕解锁时 + 电源连接时 + 电源断开时 + 网络连接变化时 + 新应用安装时 + 应用卸载时 + 应用更新时 + 耳机插拔时 + 每分钟一次 + 某些设置(屏幕方向,地区等)更改时 diff --git a/autojs/src/main/assets/modules/__ui__.js b/autojs/src/main/assets/modules/__ui__.js index ba603891..e96a4b96 100644 --- a/autojs/src/main/assets/modules/__ui__.js +++ b/autojs/src/main/assets/modules/__ui__.js @@ -19,7 +19,7 @@ module.exports = function (runtime, global) { xml = xml.toXMLString(); } runtime.ui.layoutInflater.setContext(activity); - var view = runtime.ui.layoutInflater.inflate(xml); + var view = runtime.ui.layoutInflater.inflate(xml, activity.window.decorView, false); ui.setContentView(view); } @@ -80,7 +80,7 @@ module.exports = function (runtime, global) { ui.post = function (action, delay) { delay = delay || 0; - runtime.getUiHandler().postDelay(wrapUiAction(action), delay); + runtime.getUiHandler().postDelayed(wrapUiAction(action), delay); } ui.statusBarColor = function (color) { diff --git a/autojs/src/main/java/com/stardust/autojs/AutoJs.java b/autojs/src/main/java/com/stardust/autojs/AutoJs.java index 2f95ba50..90f47f67 100644 --- a/autojs/src/main/java/com/stardust/autojs/AutoJs.java +++ b/autojs/src/main/java/com/stardust/autojs/AutoJs.java @@ -65,6 +65,7 @@ public abstract class AutoJs { mNotificationObserver = new AccessibilityNotificationObserver(mContext); mAccessibilityInfoProvider = new AccessibilityInfoProvider(mContext.getPackageManager()); mScriptEngineService = buildScriptEngineService(); + ScriptEngineService.setInstance(mScriptEngineService); init(); } diff --git a/autojs/src/main/java/com/stardust/autojs/ScriptEngineService.java b/autojs/src/main/java/com/stardust/autojs/ScriptEngineService.java index 7201205f..129b85da 100644 --- a/autojs/src/main/java/com/stardust/autojs/ScriptEngineService.java +++ b/autojs/src/main/java/com/stardust/autojs/ScriptEngineService.java @@ -67,9 +67,7 @@ public class ScriptEngineService { } private void onFinish(ScriptExecution execution) { - if (execution.getEngine() instanceof JavaScriptEngine) { - ((JavaScriptEngine) execution.getEngine()).getRuntime().onExit(); - } + } @Override @@ -87,6 +85,7 @@ public class ScriptEngineService { }; + private static ScriptEngineService sInstance; private final Context mContext; private UiHandler mUiHandler; private final Console mGlobalConsole; @@ -211,6 +210,18 @@ public class ScriptEngineService { return mScriptExecutions.get(id); } + public static void setInstance(ScriptEngineService service) { + if (sInstance != null) { + throw new IllegalStateException(); + } + sInstance = service; + } + + public static ScriptEngineService getInstance() { + return sInstance; + } + + private static class EngineLifecycleObserver implements ScriptEngineManager.EngineLifecycleCallback { private final Set mEngineLifecycleCallbacks = new LinkedHashSet<>(); diff --git a/autojs/src/main/java/com/stardust/autojs/codegeneration/ReadOnlyUiObject.java b/autojs/src/main/java/com/stardust/autojs/codegeneration/ReadOnlyUiObject.java index c2fb2758..56aac9e5 100644 --- a/autojs/src/main/java/com/stardust/autojs/codegeneration/ReadOnlyUiObject.java +++ b/autojs/src/main/java/com/stardust/autojs/codegeneration/ReadOnlyUiObject.java @@ -17,12 +17,12 @@ public class ReadOnlyUiObject extends UiObject { private NodeInfo mNodeInfo; public ReadOnlyUiObject(NodeInfo info) { - super(info, info.depth, -1); + super(null, info.depth, -1); mNodeInfo = info; } public ReadOnlyUiObject(NodeInfo info, int indexInParent) { - super(info, info.depth, indexInParent); + super(null, info.depth, indexInParent); mNodeInfo = info; } diff --git a/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.java b/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.java index 203d4857..39a76b16 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.java +++ b/autojs/src/main/java/com/stardust/autojs/core/looper/Loopers.java @@ -5,7 +5,6 @@ import android.os.Looper; import android.os.MessageQueue; import android.util.Log; -import com.android.dx.util.IntSet; import com.stardust.autojs.runtime.ScriptRuntime; import com.stardust.autojs.runtime.api.Threads; import com.stardust.autojs.runtime.api.Timers; @@ -33,7 +32,7 @@ public class Loopers implements MessageQueue.IdleHandler { private volatile ThreadLocal waitWhenIdle = new ThreadLocal<>(); private volatile ThreadLocal> waitIds = new ThreadLocal<>(); private volatile ThreadLocal maxWaitId = new ThreadLocal<>(); - private volatile ThreadLocal> looperQuitHanders = new ThreadLocal<>(); + private volatile ThreadLocal> looperQuitHandlers = new ThreadLocal<>(); private volatile Looper mServantLooper; private Timers mTimers; private ScriptRuntime mScriptRuntime; @@ -58,17 +57,17 @@ public class Loopers implements MessageQueue.IdleHandler { return mMainLooper; } - public void addLooperQuiteHandler(LooperQuitHandler handler) { - CopyOnWriteArrayList handlers = looperQuitHanders.get(); + public void addLooperQuitHandler(LooperQuitHandler handler) { + CopyOnWriteArrayList handlers = looperQuitHandlers.get(); if (handlers == null) { handlers = new CopyOnWriteArrayList<>(); - looperQuitHanders.set(handlers); + looperQuitHandlers.set(handlers); } handlers.add(handler); } - public boolean removeLooperQuiteHandler(LooperQuitHandler handler) { - CopyOnWriteArrayList handlers = looperQuitHanders.get(); + public boolean removeLooperQuitHandler(LooperQuitHandler handler) { + CopyOnWriteArrayList handlers = looperQuitHandlers.get(); return handlers != null && handlers.remove(handler); } @@ -82,7 +81,7 @@ public class Loopers implements MessageQueue.IdleHandler { if (waitWhenIdle.get() || !waitIds.get().isEmpty()) { return false; } - CopyOnWriteArrayList handlers = looperQuitHanders.get(); + CopyOnWriteArrayList handlers = looperQuitHandlers.get(); if (handlers == null) { return true; } diff --git a/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/BlockedMaterialDialog.java b/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/BlockedMaterialDialog.java index ed748eb8..b84f30df 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/BlockedMaterialDialog.java +++ b/autojs/src/main/java/com/stardust/autojs/core/ui/dialog/BlockedMaterialDialog.java @@ -136,7 +136,7 @@ public class BlockedMaterialDialog extends MaterialDialog { } public MaterialDialog.Builder itemsCallbackMultiChoice(@Nullable Integer[] selectedIndices) { - dismissListener(dialog -> setAndNotify(new Integer[0])); + dismissListener(dialog -> setAndNotify(new int[0])); super.itemsCallbackMultiChoice(selectedIndices, (dialog, which, text) -> { setAndNotify(ArrayUtils.unbox(which)); return true; diff --git a/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/DynamicLayoutInflater.java b/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/DynamicLayoutInflater.java index 6d1f1e17..a205c004 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/DynamicLayoutInflater.java +++ b/autojs/src/main/java/com/stardust/autojs/core/ui/inflater/DynamicLayoutInflater.java @@ -146,20 +146,24 @@ public class DynamicLayoutInflater { } public View inflate(String xml, @Nullable ViewGroup parent) { + return inflate(xml, parent, parent != null); + } + + public View inflate(String xml, @Nullable ViewGroup parent, boolean attachToParent) { View view = mLayoutInflaterDelegate.beforeInflation(xml, parent); if (view != null) return view; xml = convertXml(xml); - return mLayoutInflaterDelegate.afterInflation(doInflation(xml, parent), xml, parent); + return mLayoutInflaterDelegate.afterInflation(doInflation(xml, parent, attachToParent), xml, parent); } - protected View doInflation(String xml, @Nullable ViewGroup parent) { + protected View doInflation(String xml, @Nullable ViewGroup parent, boolean attachToParent) { try { DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder db = dbf.newDocumentBuilder(); Document document = db.parse(new ByteArrayInputStream(xml.getBytes())); - return inflate(document.getDocumentElement(), parent, true); + return inflate(document.getDocumentElement(), parent, attachToParent); } catch (Exception e) { throw new InflateException(e); } diff --git a/autojs/src/main/java/com/stardust/autojs/core/ui/widget/JsImageView.java b/autojs/src/main/java/com/stardust/autojs/core/ui/widget/JsImageView.java index f37dcedf..a7317b39 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/ui/widget/JsImageView.java +++ b/autojs/src/main/java/com/stardust/autojs/core/ui/widget/JsImageView.java @@ -51,7 +51,7 @@ public class JsImageView extends RoundedImageView { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); - setMeasuredDimension(getMeasuredWidth(), getMeasuredWidth()); + setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight()); if (mCircle) { setCornerRadius(getMeasuredWidth() / 2); } diff --git a/autojs/src/main/java/com/stardust/autojs/engine/JavaScriptEngine.java b/autojs/src/main/java/com/stardust/autojs/engine/JavaScriptEngine.java index abd7022d..957d4c69 100644 --- a/autojs/src/main/java/com/stardust/autojs/engine/JavaScriptEngine.java +++ b/autojs/src/main/java/com/stardust/autojs/engine/JavaScriptEngine.java @@ -45,6 +45,12 @@ public abstract class JavaScriptEngine extends ScriptEngine.AbstractScriptEngine return (ScriptSource) getTag(TAG_SOURCE); } + @Override + public synchronized void destroy() { + mRuntime.onExit(); + super.destroy(); + } + @Override public String toString() { return "ScriptEngine@" + Integer.toHexString(hashCode()) + "{" + diff --git a/autojs/src/main/java/com/stardust/autojs/engine/ScriptEngine.java b/autojs/src/main/java/com/stardust/autojs/engine/ScriptEngine.java index f0a2c09a..dca86616 100644 --- a/autojs/src/main/java/com/stardust/autojs/engine/ScriptEngine.java +++ b/autojs/src/main/java/com/stardust/autojs/engine/ScriptEngine.java @@ -5,6 +5,7 @@ import android.support.annotation.CallSuper; import com.stardust.autojs.execution.ScriptExecution; import com.stardust.autojs.script.ScriptSource; +import java.util.HashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; @@ -68,7 +69,7 @@ public interface ScriptEngine { abstract class AbstractScriptEngine implements ScriptEngine { - private Map mTags = new ConcurrentHashMap<>(); + private Map mTags = new HashMap<>(); private OnDestroyListener mOnDestroyListener; private boolean mDestroyed = false; private Exception mUncaughtException; @@ -76,9 +77,11 @@ public interface ScriptEngine { @Override public synchronized void setTag(String key, Object value) { - if (value == null) - return; - mTags.put(key, value); + if (value == null) { + mTags.remove(key); + } else { + mTags.put(key, value); + } } @Override diff --git a/autojs/src/main/java/com/stardust/autojs/execution/RunnableScriptExecution.java b/autojs/src/main/java/com/stardust/autojs/execution/RunnableScriptExecution.java index 5073803b..ce87eebc 100644 --- a/autojs/src/main/java/com/stardust/autojs/execution/RunnableScriptExecution.java +++ b/autojs/src/main/java/com/stardust/autojs/execution/RunnableScriptExecution.java @@ -6,6 +6,7 @@ import com.stardust.autojs.engine.ScriptEngine; import com.stardust.autojs.engine.ScriptEngineManager; import com.stardust.autojs.runtime.exception.ScriptInterruptedException; import com.stardust.autojs.script.ScriptSource; +import com.stardust.lang.ThreadCompat; /** * Created by Stardust on 2017/5/1. @@ -24,6 +25,7 @@ public class RunnableScriptExecution extends ScriptExecution.AbstractScriptExecu @Override public void run() { + ThreadCompat.currentThread().setName("ScriptThread-" + getId() + "[" + getSource() + "]"); execute(); } diff --git a/autojs/src/main/java/com/stardust/autojs/execution/ScriptExecuteActivity.java b/autojs/src/main/java/com/stardust/autojs/execution/ScriptExecuteActivity.java index e4f6fb94..cdfc3db3 100644 --- a/autojs/src/main/java/com/stardust/autojs/execution/ScriptExecuteActivity.java +++ b/autojs/src/main/java/com/stardust/autojs/execution/ScriptExecuteActivity.java @@ -7,11 +7,13 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuItem; import android.view.MotionEvent; +import com.stardust.autojs.ScriptEngineService; import com.stardust.autojs.core.eventloop.EventEmitter; import com.stardust.autojs.core.eventloop.SimpleEvent; import com.stardust.autojs.engine.JavaScriptEngine; @@ -21,7 +23,6 @@ import com.stardust.autojs.engine.ScriptEngineManager; import com.stardust.autojs.runtime.ScriptRuntime; import com.stardust.autojs.runtime.api.UI; import com.stardust.autojs.script.ScriptSource; -import com.stardust.util.IntentExtras; import org.mozilla.javascript.NativeObject; @@ -32,13 +33,13 @@ import org.mozilla.javascript.NativeObject; public class ScriptExecuteActivity extends AppCompatActivity { - private static final String EXTRA_EXECUTION = ScriptExecuteActivity.class.getName() + ".execution"; + private static final String LOG_TAG = "ScriptExecuteActivity"; + private static final String EXTRA_EXECUTION_ID = ScriptExecuteActivity.class.getName() + ".execution_id"; private Object mResult; private ScriptEngine mScriptEngine; private ScriptExecutionListener mExecutionListener; private ScriptSource mScriptSource; private ActivityScriptExecution mScriptExecution; - private IntentExtras mIntentExtras; private ScriptRuntime mRuntime; @@ -47,10 +48,8 @@ public class ScriptExecuteActivity extends AppCompatActivity { public static ActivityScriptExecution execute(Context context, ScriptEngineManager manager, ScriptExecutionTask task) { ActivityScriptExecution execution = new ActivityScriptExecution(manager, task); Intent i = new Intent(context, ScriptExecuteActivity.class) + .putExtra(EXTRA_EXECUTION_ID, execution.getId()) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - IntentExtras.newExtras() - .put(EXTRA_EXECUTION, execution) - .putInIntent(i); context.startActivity(i); return execution; } @@ -59,36 +58,30 @@ public class ScriptExecuteActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mIntentExtras = readIntentExtras(savedInstanceState); - if (mIntentExtras == null || mIntentExtras.get(EXTRA_EXECUTION) == null) { - finish(); + int executionId = getIntent().getIntExtra(EXTRA_EXECUTION_ID, ScriptExecution.NO_ID); + if (executionId == ScriptExecution.NO_ID) { + super.finish(); return; } - mScriptExecution = mIntentExtras.get(EXTRA_EXECUTION); + ScriptExecution execution = ScriptEngineService.getInstance().getScriptExecution(executionId); + if (execution == null || !(execution instanceof ActivityScriptExecution)) { + super.finish(); + return; + } + mScriptExecution = (ActivityScriptExecution) execution; mScriptSource = mScriptExecution.getSource(); mScriptEngine = mScriptExecution.createEngine(this); mExecutionListener = mScriptExecution.getListener(); mRuntime = ((JavaScriptEngine) mScriptEngine).getRuntime(); mEventEmitter = new EventEmitter(mRuntime.bridges); runScript(); + emit("create", savedInstanceState); } public EventEmitter getEventEmitter() { return mEventEmitter; } - private IntentExtras readIntentExtras(Bundle savedInstanceState) { - IntentExtras extras = IntentExtras.fromIntentAndRelease(getIntent()); - if (extras == null && savedInstanceState != null) { - int id = savedInstanceState.getInt(IntentExtras.EXTRA_ID, -1); - if (id == -1) { - return null; - } - extras = IntentExtras.fromIdAndRelease(id); - } - return extras; - } - private void runScript() { try { prepare(); @@ -130,6 +123,10 @@ public class ScriptExecuteActivity extends AppCompatActivity { @Override public void finish() { + if (mScriptExecution == null || mExecutionListener == null) { + super.finish(); + return; + } Exception exception = mScriptEngine.getUncaughtException(); if (exception != null) { onException(exception); @@ -142,6 +139,7 @@ public class ScriptExecuteActivity extends AppCompatActivity { @Override protected void onDestroy() { super.onDestroy(); + Log.d(LOG_TAG, "onDestroy"); mScriptEngine.put("activity", null); mScriptEngine.setTag("activity", null); mScriptEngine.destroy(); @@ -151,10 +149,8 @@ public class ScriptExecuteActivity extends AppCompatActivity { @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); - if (mIntentExtras == null) - return; - IntentExtras extras = IntentExtras.newExtras().putAll(mIntentExtras); - outState.putInt(IntentExtras.EXTRA_ID, extras.getId()); + if (mScriptExecution != null) + outState.putInt(EXTRA_EXECUTION_ID, mScriptExecution.getId()); emit("save_instance_state", outState); } diff --git a/autojs/src/main/java/com/stardust/autojs/project/ProjectConfig.java b/autojs/src/main/java/com/stardust/autojs/project/ProjectConfig.java index b7d08797..a2ac8061 100644 --- a/autojs/src/main/java/com/stardust/autojs/project/ProjectConfig.java +++ b/autojs/src/main/java/com/stardust/autojs/project/ProjectConfig.java @@ -88,7 +88,6 @@ public class ProjectConfig { try { return fromJson(PFiles.read(context.getAssets().open(path))); } catch (Exception e) { - e.printStackTrace(); return null; } } @@ -97,7 +96,6 @@ public class ProjectConfig { try { return fromJson(PFiles.read(path)); } catch (Exception e) { - e.printStackTrace(); return null; } } diff --git a/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java b/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java index 0e9363bc..4538c348 100644 --- a/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java +++ b/autojs/src/main/java/com/stardust/autojs/rhino/AndroidClassLoader.java @@ -3,6 +3,7 @@ package com.stardust.autojs.rhino; import android.util.Log; import com.android.dx.command.dexer.Main; +import com.android.dx.dex.file.DexFile; import net.lingala.zip4j.core.ZipFile; import net.lingala.zip4j.exception.ZipException; diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java b/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java index 525c45de..a8493e44 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java @@ -5,6 +5,7 @@ import android.app.Activity; import android.content.Context; import android.os.Build; import android.os.Looper; +import android.util.Log; import com.stardust.app.GlobalAppContext; import com.stardust.autojs.R; @@ -333,7 +334,7 @@ public class ScriptRuntime { } } - public void loadDex(String path){ + public void loadDex(String path) { path = files.path(path); try { ((AndroidClassLoader) ContextFactory.getGlobal().getApplicationClassLoader()).loadDex(new File(path)); @@ -375,6 +376,7 @@ public class ScriptRuntime { } public void onExit() { + Log.d(TAG, "on exit"); //清除interrupt状态 ThreadCompat.interrupted(); //悬浮窗需要第一时间关闭以免出现恶意脚本全屏悬浮窗屏蔽屏幕并且在exit中写死循环的问题 @@ -398,6 +400,7 @@ public class ScriptRuntime { } ignoresException(sensors::unregisterAll); ignoresException(timers::recycle); + ignoresException(ui::recycle); } private void ignoresException(Runnable r) { diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Sensors.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Sensors.java index a2964dee..e011e6f4 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Sensors.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Sensors.java @@ -11,13 +11,12 @@ import com.stardust.autojs.core.eventloop.EventEmitter; import com.stardust.autojs.core.looper.Loopers; import com.stardust.autojs.runtime.ScriptBridges; import com.stardust.autojs.runtime.ScriptRuntime; -import com.stardust.util.MapEntries; +import com.stardust.util.MapBuilder; import java.lang.reflect.Field; import java.util.HashSet; import java.util.Map; import java.util.Set; -import java.util.concurrent.CopyOnWriteArraySet; /** * Created by Stardust on 2018/2/5. @@ -60,21 +59,21 @@ public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler { } - private static final Map SENSORS = new MapEntries() - .entry("ACCELEROMETER", Sensor.TYPE_ACCELEROMETER) - .entry("MAGNETIC_FIELD", Sensor.TYPE_MAGNETIC_FIELD) - .entry("ORIENTATION", Sensor.TYPE_ORIENTATION) - .entry("GYROSCOPE", Sensor.TYPE_GYROSCOPE) - .entry("LIGHT", Sensor.TYPE_LIGHT) - .entry("TEMPERATURE", Sensor.TYPE_TEMPERATURE) - .entry("PRESSURE", Sensor.TYPE_PRESSURE) - .entry("AMBIENT_TEMPERATURE", Sensor.TYPE_AMBIENT_TEMPERATURE) - .entry("PROXIMITY", Sensor.TYPE_PROXIMITY) - .entry("GRAVITY", Sensor.TYPE_GRAVITY) - .entry("LINEAR_ACCELERATION", Sensor.TYPE_LINEAR_ACCELERATION) - .entry("RELATIVE_HUMIDITY", Sensor.TYPE_RELATIVE_HUMIDITY) - .entry("AMBIENT_TEMPERATURE", Sensor.TYPE_AMBIENT_TEMPERATURE) - .map(); + private static final Map SENSORS = new MapBuilder() + .put("ACCELEROMETER", Sensor.TYPE_ACCELEROMETER) + .put("MAGNETIC_FIELD", Sensor.TYPE_MAGNETIC_FIELD) + .put("ORIENTATION", Sensor.TYPE_ORIENTATION) + .put("GYROSCOPE", Sensor.TYPE_GYROSCOPE) + .put("LIGHT", Sensor.TYPE_LIGHT) + .put("TEMPERATURE", Sensor.TYPE_TEMPERATURE) + .put("PRESSURE", Sensor.TYPE_PRESSURE) + .put("AMBIENT_TEMPERATURE", Sensor.TYPE_AMBIENT_TEMPERATURE) + .put("PROXIMITY", Sensor.TYPE_PROXIMITY) + .put("GRAVITY", Sensor.TYPE_GRAVITY) + .put("LINEAR_ACCELERATION", Sensor.TYPE_LINEAR_ACCELERATION) + .put("RELATIVE_HUMIDITY", Sensor.TYPE_RELATIVE_HUMIDITY) + .put("AMBIENT_TEMPERATURE", Sensor.TYPE_AMBIENT_TEMPERATURE) + .build(); public boolean ignoresUnsupportedSensor = false; public final Delay delay = new Delay(); @@ -92,7 +91,7 @@ public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler { mScriptBridges = runtime.bridges; mNoOpSensorEventEmitter = new SensorEventEmitter(runtime.bridges); mScriptRuntime = runtime; - runtime.loopers.addLooperQuiteHandler(this); + runtime.loopers.addLooperQuitHandler(this); } public SensorEventEmitter register(String sensorName) { @@ -167,5 +166,6 @@ public class Sensors extends EventEmitter implements Loopers.LooperQuitHandler { } mSensorEventEmitters.clear(); } + mScriptRuntime.loopers.removeLooperQuitHandler(this); } } diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Threads.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Threads.java index abb91107..d1a1428c 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Threads.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Threads.java @@ -46,7 +46,7 @@ public class Threads { TimerThread thread = createThread(runnable); synchronized (mThreads) { mThreads.add(thread); - thread.setName(thread.getName() + " (Spawn-" + mSpawnCount + ")"); + thread.setName(mMainThread.getName() + " (Spawn-" + mSpawnCount + ")"); mSpawnCount++; } thread.start(); diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/UI.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/UI.java index 3ee193cd..e7b3980a 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/UI.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/UI.java @@ -113,6 +113,10 @@ public class UI extends ProxyObject { } } + public void recycle(){ + mDynamicLayoutInflater.setContext(null); + } + private class Drawables extends com.stardust.autojs.core.ui.inflater.util.Drawables { @Override diff --git a/autojs/src/main/java/com/stardust/autojs/script/JavaScriptSource.java b/autojs/src/main/java/com/stardust/autojs/script/JavaScriptSource.java index c6008135..93e8f0fa 100644 --- a/autojs/src/main/java/com/stardust/autojs/script/JavaScriptSource.java +++ b/autojs/src/main/java/com/stardust/autojs/script/JavaScriptSource.java @@ -3,7 +3,7 @@ package com.stardust.autojs.script; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import com.stardust.util.MapEntries; +import com.stardust.util.MapBuilder; import java.io.Reader; import java.io.StringReader; @@ -23,10 +23,10 @@ public abstract class JavaScriptSource extends ScriptSource { public static final int EXECUTION_MODE_UI = 0x00000001; public static final int EXECUTION_MODE_AUTO = 0x00000002; - private static final Map EXECUTION_MODES = new MapEntries() - .entry("ui", EXECUTION_MODE_UI) - .entry("auto", EXECUTION_MODE_AUTO) - .map(); + private static final Map EXECUTION_MODES = new MapBuilder() + .put("ui", EXECUTION_MODE_UI) + .put("auto", EXECUTION_MODE_AUTO) + .build(); private static final int EXECUTION_MODE_STRING_MAX_LENGTH = 7; private int mExecutionMode = -1; diff --git a/autojs/src/main/java/com/stardust/autojs/script/JsBeautifier.java b/autojs/src/main/java/com/stardust/autojs/script/JsBeautifier.java index 15b02ffa..5b114468 100644 --- a/autojs/src/main/java/com/stardust/autojs/script/JsBeautifier.java +++ b/autojs/src/main/java/com/stardust/autojs/script/JsBeautifier.java @@ -19,6 +19,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** @@ -35,7 +36,7 @@ public class JsBeautifier { void onException(Exception e); } - private Executor mExecutor = Executors.newSingleThreadExecutor(); + private ExecutorService mExecutor = Executors.newSingleThreadExecutor(); private Context mContext; private Function mJsBeautifyFunction; private org.mozilla.javascript.Context mScriptContext; @@ -122,4 +123,9 @@ public class JsBeautifier { } } + public void shutdown(){ + mExecutor.shutdownNow(); + mView = null; + } + } diff --git a/autojs/src/main/java/com/stardust/autojs/script/ScriptSource.java b/autojs/src/main/java/com/stardust/autojs/script/ScriptSource.java index 53303a1a..b66d3b74 100644 --- a/autojs/src/main/java/com/stardust/autojs/script/ScriptSource.java +++ b/autojs/src/main/java/com/stardust/autojs/script/ScriptSource.java @@ -1,17 +1,6 @@ package com.stardust.autojs.script; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.view.View; - -import com.stardust.util.MapEntries; - -import java.io.Reader; import java.io.Serializable; -import java.io.StringReader; -import java.util.Arrays; -import java.util.List; -import java.util.Map; /** * Created by Stardust on 2017/4/2. diff --git a/autojs/src/main/java/org/mozilla/javascript/VMBridge_custom.java b/autojs/src/main/java/org/mozilla/javascript/VMBridge_custom.java new file mode 100644 index 00000000..521b1084 --- /dev/null +++ b/autojs/src/main/java/org/mozilla/javascript/VMBridge_custom.java @@ -0,0 +1,118 @@ +package org.mozilla.javascript; + +import android.os.Looper; +import android.util.Log; + +import org.mozilla.javascript.jdk15.VMBridge_jdk15; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class VMBridge_custom extends VMBridge_jdk15 { + + private static final String LOG_TAG = "VMBridge_custom"; + + public VMBridge_custom() throws SecurityException, InstantiationException { + } + + @Override + protected Object newInterfaceProxy(Object proxyHelper, ContextFactory cf, InterfaceAdapter adapter, Object target, Scriptable topScope) { + // --- The following code is copied from super class -- + Constructor c = (Constructor) proxyHelper; + InvocationHandler handler = (proxy, method, args) -> { + if (method.getDeclaringClass() == Object.class) { + String methodName = method.getName(); + if (methodName.equals("equals")) { + Object other = args[0]; + return proxy == other; + } + + if (methodName.equals("hashCode")) { + return target.hashCode(); + } + + if (methodName.equals("toString")) { + return "Proxy[" + target.toString() + "]"; + } + } + // Add thread check + //Check if the current thread is ui thread + if (Looper.myLooper() == Looper.getMainLooper()) { + // If so, catch any exception of invoking + // Because an exception on ui thread will cause the whole app to crash + try { + Object result = adapter.invoke(cf, target, topScope, proxy, method, args); + return castReturnValue(method, result); + } catch (Exception e) { + e.printStackTrace(); + // notify the script thread to exit + Object jsRuntime = topScope.get("runtime", null); + Log.d(LOG_TAG, "jsRuntime = " + jsRuntime); + if (jsRuntime instanceof NativeJavaObject) { + Object runtime = ((NativeJavaObject) jsRuntime).unwrap(); + Log.d(LOG_TAG, "runtime = " + runtime); + if(runtime instanceof com.stardust.autojs.runtime.ScriptRuntime){ + ((com.stardust.autojs.runtime.ScriptRuntime) runtime).exit(e); + } + } + // even if we caught the exception, we must return a value to for the method call. + return defaultValue(method.getReturnType()); + } + } else { + return castReturnValue(method, adapter.invoke(cf, target, topScope, proxy, method, args)); + } + }; + + // --- The following code is copied from super class -- + try { + Object proxy = c.newInstance(handler); + return proxy; + } catch (InvocationTargetException var10) { + throw Context.throwAsScriptRuntimeEx(var10); + } catch (IllegalAccessException var11) { + throw Kit.initCause(new IllegalStateException(), var11); + } catch (InstantiationException var12) { + throw Kit.initCause(new IllegalStateException(), var12); + } + } + + // cast the return value to boolean if needed. + // if a javascript function that implements a java interface returns nothing, + // it will be regarded as "false", like javascript behavior, instead of reporting error "undefined cannot be cast to boolean" + protected Object castReturnValue(Method method, Object returnValue) { + if (method.getReturnType().equals(Boolean.TYPE) || method.getReturnType().equals(Boolean.class)) { + return ScriptRuntime.toBoolean(returnValue); + } + return returnValue; + } + + protected Object defaultValue(Class type) { + if (type.equals(Boolean.TYPE) || type.equals(Boolean.class)) { + return false; + } + if (type.equals(Integer.TYPE) || type.equals(Integer.class)) { + return 0; + } + if (type.equals(Long.TYPE) || type.equals(Long.class)) { + return 0L; + } + if (type.equals(Float.TYPE) || type.equals(Float.class)) { + return 0F; + } + if (type.equals(Double.TYPE) || type.equals(Double.class)) { + return 0.0; + } + if (type.equals(Byte.TYPE) || type.equals(Byte.class)) { + return (byte) 0; + } + if (type.equals(Character.TYPE) || type.equals(Character.class)) { + return (char) 0; + } + if (type.isAssignableFrom(CharSequence.class)) { + return ""; + } + return null; + } +} diff --git a/automator/src/main/java/com/stardust/automator/simple_action/ActionFactory.java b/automator/src/main/java/com/stardust/automator/simple_action/ActionFactory.java index 50c27c2f..e18fe2e9 100644 --- a/automator/src/main/java/com/stardust/automator/simple_action/ActionFactory.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/ActionFactory.java @@ -7,7 +7,7 @@ import android.support.annotation.RequiresApi; import android.view.accessibility.AccessibilityNodeInfo; import com.stardust.automator.UiObject; -import com.stardust.util.MapEntries; +import com.stardust.util.MapBuilder; import java.util.Map; @@ -17,15 +17,15 @@ import java.util.Map; public class ActionFactory { - private static Map searchUpAction = new MapEntries() - .entry(AccessibilityNodeInfo.ACTION_CLICK, null) - .entry(AccessibilityNodeInfo.ACTION_LONG_CLICK, null) - .entry(AccessibilityNodeInfo.ACTION_SELECT, null) - .entry(AccessibilityNodeInfo.ACTION_FOCUS, null) - .entry(AccessibilityNodeInfo.ACTION_SELECT, null) - .entry(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, null) - .entry(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null) - .map(); + private static Map searchUpAction = new MapBuilder() + .put(AccessibilityNodeInfo.ACTION_CLICK, null) + .put(AccessibilityNodeInfo.ACTION_LONG_CLICK, null) + .put(AccessibilityNodeInfo.ACTION_SELECT, null) + .put(AccessibilityNodeInfo.ACTION_FOCUS, null) + .put(AccessibilityNodeInfo.ACTION_SELECT, null) + .put(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, null) + .put(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null) + .build(); public static SimpleAction createActionWithTextFilter(int action, String text, int index) { if (searchUpAction.containsKey(action)) diff --git a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java index 203410d3..5a993948 100644 --- a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java +++ b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityService.java @@ -24,7 +24,6 @@ import java.util.concurrent.locks.ReentrantLock; public class AccessibilityService extends android.accessibilityservice.AccessibilityService { - private static final String TAG = "AccessibilityService"; private static final SortedMap mDelegates = new TreeMap<>(); @@ -55,13 +54,13 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi @Override public void onAccessibilityEvent(final AccessibilityEvent event) { instance = this; - Log.v(TAG, "onAccessibilityEvent: " + event); + //Log.v(TAG, "onAccessibilityEvent: " + event); if (!containsAllEventTypes && !eventTypes.contains(event.getEventType())) return; int type = event.getEventType(); if (type == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED || type == AccessibilityEvent.TYPE_VIEW_FOCUSED) { - AccessibilityNodeInfo root = super.getRootInActiveWindow(); + AccessibilityNodeInfo root = getRootInActiveWindow(); if (root != null) { mFastRootInActiveWindow = root; } @@ -109,7 +108,7 @@ public class AccessibilityService extends android.accessibilityservice.Accessibi public AccessibilityNodeInfo getRootInActiveWindow() { try { return super.getRootInActiveWindow(); - } catch (IllegalStateException e) { + } catch (Exception e) { return null; } } diff --git a/common/release/output.json b/common/release/output.json index 9b80d418..25bb428d 100644 --- a/common/release/output.json +++ b/common/release/output.json @@ -1 +1 @@ -[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":416},"path":"commonRelease-4.0.2 Alpha11.apk","properties":{"packageId":"org.autojs.autojs","split":"","minSdkVersion":"17"}}] \ No newline at end of file +[{"outputType":{"type":"APK"},"apkInfo":{"type":"MAIN","splits":[],"versionCode":421},"path":"commonRelease-4.0.3 Alpha2.apk","properties":{"packageId":"org.autojs.autojs","split":"","minSdkVersion":"17"}}] \ No newline at end of file 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); + } + } +} diff --git a/common/src/main/java/com/stardust/util/MapEntries.java b/common/src/main/java/com/stardust/util/MapBuilder.java similarity index 66% rename from common/src/main/java/com/stardust/util/MapEntries.java rename to common/src/main/java/com/stardust/util/MapBuilder.java index 588ed5a5..3f465b81 100644 --- a/common/src/main/java/com/stardust/util/MapEntries.java +++ b/common/src/main/java/com/stardust/util/MapBuilder.java @@ -2,30 +2,29 @@ package com.stardust.util; import java.util.HashMap; import java.util.Map; -import java.util.TreeMap; /** * Created by Stardust on 2017/1/26. */ -public class MapEntries { +public class MapBuilder { private Map mMap; - public MapEntries() { + public MapBuilder() { this(new HashMap()); } - public MapEntries(Map map) { + public MapBuilder(Map map) { mMap = map; } - public MapEntries entry(K key, V value) { + public MapBuilder put(K key, V value) { mMap.put(key, value); return this; } - public Map map() { + public Map build() { return mMap; }