diff --git a/.idea/misc.xml b/.idea/misc.xml index c44fe40c..db1ea67d 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -2,14 +2,15 @@ - - - - - - - - + + + + + + + + + diff --git a/app/build.gradle b/app/build.gradle index 24b5acc7..b4a4821b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,7 +16,8 @@ android { } buildTypes { release { - minifyEnabled false + shrinkResources true + minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } @@ -70,7 +71,7 @@ dependencies { compile 'io.mattcarroll.hover:hover:0.9.7' compile 'org.greenrobot:eventbus:3.0.0' compile 'com.bignerdranch.android:expandablerecyclerview:3.0.0-RC1' - compile 'com.ashokvarma.android:bottom-navigation-bar:1.4.1' + compile 'com.ashokvarma.android:bottom-navigation-bar:1.4.1' compile 'com.yqritc:recyclerview-flexibledivider:1.4.0' compile 'me.zhanghai.android.materialprogressbar:library:1.3.0' compile group:'com.twofortyfouram', name:'android-plugin-client-sdk-for-locale', version:'[4.0.2, 5.0[' diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index cb27ab2e..d781ed04 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -15,3 +15,12 @@ #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} + +-dontwarn org.mozilla.javascript.** +-dontwarn com.jecelyin.editor.** +-dontwarn com.makeramen.** +-dontwarn org.junit.** +-dontwarn junit.** +-dontwarn jackpal.androidterm.** +-dontwarn com.iwebpp.nodeandroid.** +-dontwarn org.msgpack.core.** diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e5e601a1..24b32e70 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -75,12 +75,12 @@ + - diff --git a/app/src/main/java/com/stardust/app/VolumeChangeObserver.java b/app/src/main/java/com/stardust/app/VolumeChangeObserver.java new file mode 100644 index 00000000..5b547db4 --- /dev/null +++ b/app/src/main/java/com/stardust/app/VolumeChangeObserver.java @@ -0,0 +1,46 @@ +package com.stardust.app; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Stardust on 2017/4/4. + */ + +public class VolumeChangeObserver extends BroadcastReceiver { + + public interface OnVolumeChangeListener { + + void onVolumeChange(); + } + + public static final String ACTION_VOLUME_CHANGE = "android.media.VOLUME_CHANGED_ACTION"; + + private long mLastChangeMillis; + private List mOnVolumeChangeListenerList = new ArrayList<>(); + + public void addOnVolumeChangeListener(OnVolumeChangeListener listener) { + mOnVolumeChangeListenerList.add(listener); + } + + public void removeOnVolumeChangeListener(OnVolumeChangeListener listener) { + mOnVolumeChangeListenerList.remove(listener); + } + + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(ACTION_VOLUME_CHANGE)) { + if (System.currentTimeMillis() - mLastChangeMillis < 400) { + return; + } + mLastChangeMillis = System.currentTimeMillis(); + for (OnVolumeChangeListener listener : mOnVolumeChangeListenerList) { + listener.onVolumeChange(); + } + } + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/App.java b/app/src/main/java/com/stardust/scriptdroid/App.java index 6dac8c26..306910c6 100644 --- a/app/src/main/java/com/stardust/scriptdroid/App.java +++ b/app/src/main/java/com/stardust/scriptdroid/App.java @@ -3,12 +3,15 @@ package com.stardust.scriptdroid; import android.app.Activity; import android.app.Application; import android.content.Intent; +import android.content.IntentFilter; import android.os.Bundle; +import android.support.annotation.Keep; import com.squareup.leakcanary.LeakCanary; import com.stardust.app.SimpleActivityLifecycleCallbacks; +import com.stardust.app.VolumeChangeObserver; import com.stardust.scriptdroid.autojs.AutoJs; -import com.stardust.scriptdroid.service.VolumeChangeObverseService; +import com.stardust.scriptdroid.service.AccessibilityWatchDogService; import com.stardust.scriptdroid.tool.CrashHandler; import com.stardust.scriptdroid.ui.error.ErrorReportActivity; import com.stardust.theme.ThemeColor; @@ -29,6 +32,8 @@ public class App extends Application { return instance; } + private VolumeChangeObserver mVolumeChangeObserver = new VolumeChangeObserver(); + public void onCreate() { super.onCreate(); instance = this; @@ -50,7 +55,13 @@ public class App extends Application { 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); - VolumeChangeObverseService.addOnVolumeChangeListener(new VolumeChangeObverseService.OnVolumeChangeListener() { + initVolumeChangeObserver(); + startService(new Intent(this, AccessibilityWatchDogService.class)); + } + + private void initVolumeChangeObserver() { + registerReceiver(mVolumeChangeObserver, new IntentFilter(VolumeChangeObserver.ACTION_VOLUME_CHANGE)); + mVolumeChangeObserver.addOnVolumeChangeListener(new VolumeChangeObserver.OnVolumeChangeListener() { @Override public void onVolumeChange() { if (Pref.isRunningVolumeControlEnabled()) { @@ -58,7 +69,6 @@ public class App extends Application { } } }); - startService(new Intent(this, VolumeChangeObverseService.class)); } private void registerActivityLifecycleCallback() { @@ -86,6 +96,7 @@ public class App extends Application { }); } + @Keep public static Activity currentActivity() { return currentActivity; } @@ -94,4 +105,7 @@ public class App extends Application { return getApp().getString(id); } + public VolumeChangeObserver getVolumeChangeObserver() { + return mVolumeChangeObserver; + } } diff --git a/app/src/main/java/com/stardust/scriptdroid/autojs/AutoJs.java b/app/src/main/java/com/stardust/scriptdroid/autojs/AutoJs.java index 30b0aa9b..d2166353 100644 --- a/app/src/main/java/com/stardust/scriptdroid/autojs/AutoJs.java +++ b/app/src/main/java/com/stardust/scriptdroid/autojs/AutoJs.java @@ -1,15 +1,16 @@ package com.stardust.scriptdroid.autojs; +import android.accessibilityservice.AccessibilityService; import android.content.Context; +import android.support.annotation.Nullable; import com.stardust.autojs.ScriptEngineService; import com.stardust.autojs.ScriptEngineServiceBuilder; -import com.stardust.autojs.engine.NodeJsJavaScriptEngine; import com.stardust.autojs.engine.NodeJsJavaScriptEngineManager; import com.stardust.autojs.runtime.*; -import com.stardust.autojs.runtime.action.ActionPerformAccessibilityDelegate; import com.stardust.autojs.runtime.api.Console; import com.stardust.automator.AccessibilityEventCommandHost; +import com.stardust.automator.simple_action.SimpleActionPerformHost; import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.Pref; import com.stardust.scriptdroid.R; @@ -40,7 +41,7 @@ public class AutoJs implements AccessibilityBridge { } private final AccessibilityEventCommandHost mAccessibilityEventCommandHost = new AccessibilityEventCommandHost(); - private final ActionPerformAccessibilityDelegate mActionPerformAccessibilityDelegate = new ActionPerformAccessibilityDelegate(); + private final SimpleActionPerformHost mSimpleActionPerformHost = new SimpleActionPerformHost(); private final AccessibilityActionRecorder mAccessibilityActionRecorder = new AccessibilityActionRecorder(); private final LayoutInspector mLayoutInspector = new LayoutInspector(); private final ScriptEngineService mScriptEngineService; @@ -62,14 +63,24 @@ public class AutoJs implements AccessibilityBridge { .build(); ScriptEngineService.setInstance(mScriptEngineService); addAccessibilityServiceDelegates(); + getGlobalFunctions(); + } + + private void getGlobalFunctions() { + new Thread(new Runnable() { + @Override + public void run() { + mScriptEngineService.getGlobalFunctions(); + } + }).start(); } private void addAccessibilityServiceDelegates() { - AccessibilityWatchDogService.addDelegateIfNeeded(100, mActionPerformAccessibilityDelegate); - AccessibilityWatchDogService.addDelegateIfNeeded(200, mAccessibilityActionRecorder); - AccessibilityWatchDogService.addDelegateIfNeeded(300, mAccessibilityEventCommandHost); - AccessibilityWatchDogService.addDelegateIfNeeded(400, mAccessibilityInfoProvider); - AccessibilityWatchDogService.addDelegateIfNeeded(500, mLayoutInspector); + AccessibilityWatchDogService.addDelegate(100, mAccessibilityInfoProvider); + // AccessibilityWatchDogService.addDelegate(200, mLayoutInspector); + AccessibilityWatchDogService.addDelegate(300, mAccessibilityActionRecorder); + // AccessibilityWatchDogService.addDelegate(400, mSimpleActionPerformHost); + //AccessibilityWatchDogService.addDelegate(500, mAccessibilityEventCommandHost); } public AccessibilityActionRecorder getAccessibilityActionRecorder() { @@ -86,8 +97,14 @@ public class AutoJs implements AccessibilityBridge { } @Override - public ActionPerformAccessibilityDelegate getActionPerformHost() { - return mActionPerformAccessibilityDelegate; + public SimpleActionPerformHost getActionPerformHost() { + return mSimpleActionPerformHost; + } + + @Nullable + @Override + public AccessibilityService getService() { + return AccessibilityWatchDogService.getInstance(); } @Override diff --git a/app/src/main/java/com/stardust/scriptdroid/external/CommonUtils.java b/app/src/main/java/com/stardust/scriptdroid/external/CommonUtils.java index 829dd885..71c06d4e 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/CommonUtils.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/CommonUtils.java @@ -10,10 +10,10 @@ public class CommonUtils { public static final String EXTRA_KEY_PATH = "path"; - public static final String EXTRA_KEY_PREPARE_SCRIPT = "script"; + public static final String EXTRA_KEY_PRE_EXECUTE_SCRIPT = "script"; public static boolean isTaskerBundleValid(Bundle bundle) { - return bundle.containsKey(CommonUtils.EXTRA_KEY_PATH); + return bundle.containsKey(CommonUtils.EXTRA_KEY_PATH) || bundle.containsKey(EXTRA_KEY_PRE_EXECUTE_SCRIPT); } } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/HoverMenuService.java b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/HoverMenuService.java index d60a5f78..872a9e3e 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/HoverMenuService.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/HoverMenuService.java @@ -68,7 +68,7 @@ public class HoverMenuService extends Service { private static void setIsRunning(boolean isRunning) { sIsRunning = isRunning; - EventBus.getDefault().post(new ServiceStateChangedEvent(sIsRunning)); + eventBus.post(new ServiceStateChangedEvent(sIsRunning)); } public static void postEvent(MessageEvent event) { @@ -96,7 +96,7 @@ public class HoverMenuService extends Service { private HoverMenu.OnExitListener mWindowHoverMenuMenuExitListener = new HoverMenu.OnExitListener() { @Override public void onExitByUserRequest() { - EventBus.getDefault().post(new MessageEvent(MESSAGE_MENU_EXIT)); + eventBus.post(new MessageEvent(MESSAGE_MENU_EXIT)); savePreferredLocation(); mWindowHoverMenu.hide(); stopSelf(); @@ -106,7 +106,7 @@ public class HoverMenuService extends Service { @Override public void onCreate() { super.onCreate(); - EventBus.getDefault().register(this); + eventBus.register(this); mPrefs = getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); tryInitViews(); } @@ -141,14 +141,14 @@ public class HoverMenuService extends Service { mWindowHoverMenu.setHoverMenuTransitionListener(new SimpleHoverMenuTransitionListener() { @Override public void onExpanding() { - EventBus.getDefault().post(new MessageEvent(MESSAGE_MENU_EXPANDING)); + eventBus.post(new MessageEvent(MESSAGE_MENU_EXPANDING)); captureCurrentWindow(); } @Override public void onCollapsing() { AutoJs.getInstance().getLayoutInspector().clearCapture(); - EventBus.getDefault().post(new MessageEvent(MESSAGE_MENU_COLLAPSING)); + eventBus.post(new MessageEvent(MESSAGE_MENU_COLLAPSING)); } }); } @@ -182,8 +182,8 @@ public class HoverMenuService extends Service { public void onDestroy() { if (mWindowHoverMenu != null) mWindowHoverMenu.hide(); - if (EventBus.getDefault().isRegistered(this)) - EventBus.getDefault().unregister(this); + if (eventBus.isRegistered(this)) + eventBus.unregister(this); setIsRunning(false); } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/RecordNavigatorContent.java b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/RecordNavigatorContent.java index 0f72213d..b1009b36 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/RecordNavigatorContent.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floating_window/content/RecordNavigatorContent.java @@ -1,7 +1,6 @@ package com.stardust.scriptdroid.external.floating_window.content; import android.content.Context; -import android.content.Intent; import android.support.annotation.NonNull; import android.support.v7.widget.SwitchCompat; import android.view.View; @@ -9,6 +8,7 @@ import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; +import com.stardust.app.VolumeChangeObserver; import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.Pref; import com.stardust.scriptdroid.R; @@ -19,7 +19,6 @@ import com.stardust.scriptdroid.record.Recorder; import com.stardust.scriptdroid.record.accessibility.AccessibilityActionRecorder; import com.stardust.scriptdroid.record.inputevent.InputEventConverter; import com.stardust.scriptdroid.record.inputevent.TouchRecorder; -import com.stardust.scriptdroid.service.VolumeChangeObverseService; import com.stardust.scriptdroid.ui.main.MainActivity; import com.stardust.util.MessageEvent; import com.stardust.view.ViewBinder; @@ -58,7 +57,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat private Recorder mRecorder; private Context mContext; - private VolumeChangeObverseService.OnVolumeChangeListener mOnVolumeChangeListener = new VolumeChangeObverseService.OnVolumeChangeListener() { + private VolumeChangeObserver.OnVolumeChangeListener mOnVolumeChangeListener = new VolumeChangeObserver.OnVolumeChangeListener() { @Override public void onVolumeChange() { if (Pref.isRecordVolumeControlEnable()) { @@ -76,8 +75,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat mView = View.inflate(context, R.layout.floating_window_record, null); ViewBinder.bind(this); EventBus.getDefault().register(this); - App.getApp().startService(new Intent(App.getApp(), VolumeChangeObverseService.class)); - VolumeChangeObverseService.addOnVolumeChangeListener(mOnVolumeChangeListener); + App.getApp().getVolumeChangeObserver().addOnVolumeChangeListener(mOnVolumeChangeListener); } @NonNull @@ -163,7 +161,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat pauseRecord(); } else if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXIT)) { EventBus.getDefault().unregister(this); - VolumeChangeObverseService.removeOnVolumeChangeListener(mOnVolumeChangeListener); + App.getApp().getVolumeChangeObserver().removeOnVolumeChangeListener(mOnVolumeChangeListener); } } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/open/RunIntentActivity.java b/app/src/main/java/com/stardust/scriptdroid/external/open/RunIntentActivity.java index 1af7e6f4..8f4d300a 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/open/RunIntentActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/open/RunIntentActivity.java @@ -12,7 +12,6 @@ import com.stardust.autojs.script.FileScriptSource; import com.stardust.autojs.script.MultiScriptSource; import com.stardust.autojs.script.ScriptSource; import com.stardust.autojs.script.StringScriptSource; -import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.external.CommonUtils; import com.stardust.scriptdroid.R; @@ -36,7 +35,7 @@ public class RunIntentActivity extends Activity { private void handleIntent(Intent intent) { String path = getPath(intent); - String script = intent.getStringExtra(CommonUtils.EXTRA_KEY_PREPARE_SCRIPT); + String script = intent.getStringExtra(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT); ScriptSource source = null; if (path == null && script != null) { source = new StringScriptSource(script); diff --git a/app/src/main/java/com/stardust/scriptdroid/external/tasker/FireSettingReceiver.java b/app/src/main/java/com/stardust/scriptdroid/external/tasker/FireSettingReceiver.java index 16723242..5f276a43 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/tasker/FireSettingReceiver.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/tasker/FireSettingReceiver.java @@ -1,16 +1,13 @@ package com.stardust.scriptdroid.external.tasker; -import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; -import android.util.Log; import com.stardust.scriptdroid.App; import com.stardust.scriptdroid.external.CommonUtils; import com.stardust.scriptdroid.external.open.RunIntentActivity; -import com.stardust.scriptdroid.external.shortcut.ShortcutActivity; import com.twofortyfouram.locale.sdk.client.receiver.AbstractPluginSettingReceiver; /** @@ -36,6 +33,6 @@ public class FireSettingReceiver extends AbstractPluginSettingReceiver { context.startActivity(new Intent(App.getApp(), RunIntentActivity.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .putExtra(CommonUtils.EXTRA_KEY_PATH, bundle.getString(CommonUtils.EXTRA_KEY_PATH)) - .putExtra(CommonUtils.EXTRA_KEY_PREPARE_SCRIPT, bundle.getString(CommonUtils.EXTRA_KEY_PREPARE_SCRIPT))); + .putExtra(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT, bundle.getString(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT))); } } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/tasker/TaskPrefEditActivity.java b/app/src/main/java/com/stardust/scriptdroid/external/tasker/TaskPrefEditActivity.java index 37ad1217..1a0399d8 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/tasker/TaskPrefEditActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/tasker/TaskPrefEditActivity.java @@ -1,37 +1,50 @@ package com.stardust.scriptdroid.external.tasker; -import android.content.Context; +import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import android.util.Log; +import android.text.TextUtils; +import android.view.Menu; +import android.view.MenuItem; -import com.stardust.scriptdroid.scripts.ScriptFile; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.external.CommonUtils; +import com.stardust.scriptdroid.scripts.ScriptFile; +import com.stardust.scriptdroid.scripts.StorageScriptProvider; +import com.stardust.scriptdroid.ui.BaseActivity; import com.stardust.scriptdroid.ui.main.script_list.ScriptAndFolderListRecyclerView; import com.stardust.scriptdroid.ui.main.script_list.ScriptListWithProgressBarView; -import com.twofortyfouram.locale.sdk.client.ui.activity.AbstractPluginActivity; +import com.stardust.view.ViewBinder; +import com.stardust.view.ViewBinding; +import com.twofortyfouram.locale.sdk.client.ui.activity.AbstractAppCompatPluginActivity; /** * Created by Stardust on 2017/3/27. */ -public class TaskPrefEditActivity extends AbstractPluginActivity { +public class TaskPrefEditActivity extends AbstractAppCompatPluginActivity { private String mSelectedScriptFilePath; + private String mPreExecuteScript; + private StorageScriptProvider mStorageScriptProvider; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_tasker_edit); + BaseActivity.setToolbarAsBack(this, R.id.toolbar, getString(R.string.text_please_choose_a_script)); initScriptListRecyclerView(); + ViewBinder.bind(this); } private void initScriptListRecyclerView() { - ScriptListWithProgressBarView scriptListWithProgressBarView = (ScriptListWithProgressBarView) findViewById(R.id.script_list); - scriptListWithProgressBarView.setScriptFileOperationEnabled(false); - scriptListWithProgressBarView.setOnItemClickListener(new ScriptAndFolderListRecyclerView.OnScriptFileClickListener() { + mStorageScriptProvider = StorageScriptProvider.getExternalStorageProvider(); + ScriptListWithProgressBarView scriptList = (ScriptListWithProgressBarView) findViewById(R.id.script_list); + scriptList.setScriptFileOperationEnabled(false); + scriptList.setStorageScriptProvider(mStorageScriptProvider); + scriptList.setCurrentDirectory(StorageScriptProvider.DEFAULT_DIRECTORY); + scriptList.setOnItemClickListener(new ScriptAndFolderListRecyclerView.OnScriptFileClickListener() { @Override public void onClick(ScriptFile file) { mSelectedScriptFilePath = file.getPath(); @@ -41,6 +54,31 @@ public class TaskPrefEditActivity extends AbstractPluginActivity { } + @ViewBinding.Click(R.id.edit_script) + private void editPreExecuteScript() { + TaskerScriptEditActivity.edit(this, getString(R.string.text_pre_execute_script), getString(R.string.summary_pre_execute_script), mPreExecuteScript == null ? "" : mPreExecuteScript); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.action_refresh) { + mStorageScriptProvider.refreshAll(); + } else if (item.getItemId() == R.id.action_clear_file_selection) { + mSelectedScriptFilePath = null; + } else { + mPreExecuteScript = null; + } + return true; + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.tasker_script_edit_menu, menu); + return true; + } + + @Override public boolean isBundleValid(@NonNull Bundle bundle) { return CommonUtils.isTaskerBundleValid(bundle); @@ -49,6 +87,7 @@ public class TaskPrefEditActivity extends AbstractPluginActivity { @Override public void onPostCreateWithPreviousResult(@NonNull Bundle bundle, @NonNull String s) { mSelectedScriptFilePath = bundle.getString(CommonUtils.EXTRA_KEY_PATH); + mPreExecuteScript = bundle.getString(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT); } @Nullable @@ -56,12 +95,27 @@ public class TaskPrefEditActivity extends AbstractPluginActivity { public Bundle getResultBundle() { Bundle bundle = new Bundle(); bundle.putString(CommonUtils.EXTRA_KEY_PATH, mSelectedScriptFilePath); + bundle.putString(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT, mPreExecuteScript); return bundle; } @NonNull @Override public String getResultBlurb(@NonNull Bundle bundle) { - return bundle.getString(CommonUtils.EXTRA_KEY_PATH, getString(R.string.text_path_is_empty)); + String blurb = bundle.getString(CommonUtils.EXTRA_KEY_PATH); + if (TextUtils.isEmpty(blurb)) { + blurb = bundle.getString(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT); + } + if (TextUtils.isEmpty(blurb)) { + blurb = getString(R.string.text_path_is_empty); + } + return blurb; + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (resultCode == RESULT_OK) { + mPreExecuteScript = data.getStringExtra(TaskerScriptEditActivity.EXTRA_CONTENT); + } } } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/tasker/TaskerScriptEditActivity.java b/app/src/main/java/com/stardust/scriptdroid/external/tasker/TaskerScriptEditActivity.java new file mode 100644 index 00000000..896c1a18 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/external/tasker/TaskerScriptEditActivity.java @@ -0,0 +1,107 @@ +package com.stardust.scriptdroid.external.tasker; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.widget.TextView; + +import com.jecelyin.editor.v2.common.Command; +import com.jecelyin.editor.v2.ui.EditorDelegate; +import com.jecelyin.editor.v2.view.EditorView; +import com.jecelyin.editor.v2.view.menu.MenuDef; +import com.stardust.scriptdroid.R; +import com.stardust.scriptdroid.ui.BaseActivity; +import com.stardust.scriptdroid.ui.edit.EditActivity; +import com.stardust.scriptdroid.ui.edit.completion.InputMethodEnhanceBar; +import com.stardust.scriptdroid.ui.edit.editor920.Editor920Activity; +import com.stardust.scriptdroid.ui.edit.editor920.Editor920Utils; +import com.stardust.view.ViewBinder; +import com.stardust.view.ViewBinding; +import com.stardust.widget.ToolbarMenuItem; + +/** + * Created by Stardust on 2017/4/5. + */ + +public class TaskerScriptEditActivity extends Editor920Activity { + + public static final int REQUEST_CODE = "Love you. Can we go back?".hashCode() >> 16; + public static final String EXTRA_CONTENT = "Still Love Eating 17.4.5"; + + public static void edit(Activity activity, String title, String summary, String content) { + activity.startActivityForResult(new Intent(activity, TaskerScriptEditActivity.class) + .putExtra(EXTRA_CONTENT, content) + .putExtra("summary", summary) + .putExtra("title", title), REQUEST_CODE); + } + + private EditorDelegate mEditorDelegate; + private String mTitle, mSummary; + private ToolbarMenuItem mRedo, mUndo; + + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + handleIntent(getIntent()); + setUpUI(); + BaseActivity.setToolbarAsBack(this, R.id.toolbar, mTitle); + setUpEditor(); + ViewBinder.bind(this); + } + + private void setUpUI() { + setTheme(R.style.EditorTheme); + setContentView(R.layout.activity_tasker_script_edit); + ((TextView) findViewById(R.id.summary)).setText(mSummary); + mRedo = (ToolbarMenuItem) findViewById(R.id.redo); + mUndo = (ToolbarMenuItem) findViewById(R.id.undo); + } + + private void handleIntent(Intent intent) { + mTitle = intent.getStringExtra("title"); + mSummary = intent.getStringExtra("summary"); + String content = intent.getStringExtra(EXTRA_CONTENT); + mEditorDelegate = new EditorDelegate(0, mTitle, content); + } + + private void setUpEditor() { + final EditorView editorView = (EditorView) findViewById(R.id.editor); + mEditorDelegate.setEditorView(editorView); + Editor920Utils.setLang(mEditorDelegate, "JavaScript"); + editorView.getEditText().setHorizontallyScrolling(true); + setUpInputMethodEnhanceBar(editorView); + } + + private void setUpInputMethodEnhanceBar(final EditorView editorView) { + InputMethodEnhanceBar inputMethodEnhanceBar = (InputMethodEnhanceBar) findViewById(R.id.input_method_enhance_bar); + inputMethodEnhanceBar.setEditTextBridge(new EditActivity.InputMethodEnhanceBarBridge(this, editorView.getEditText())); + } + + @ViewBinding.Click(R.id.undo) + private void undo() { + Command command = new Command(Command.CommandEnum.UNDO); + mEditorDelegate.doCommand(command); + } + + @ViewBinding.Click(R.id.redo) + private void redo() { + Command command = new Command(Command.CommandEnum.REDO); + mEditorDelegate.doCommand(command); + } + + @Override + public void finish() { + setResult(RESULT_OK, new Intent().putExtra(EXTRA_CONTENT, mEditorDelegate.getText())); + super.finish(); + } + + public void setMenuStatus(int menuResId, int status) { + boolean disabled = status == MenuDef.STATUS_DISABLED; + if (menuResId == com.jecelyin.editor.v2.R.id.m_redo) { + mRedo.setEnabled(!disabled); + } else if (menuResId == com.jecelyin.editor.v2.R.id.m_undo) { + mUndo.setEnabled(!disabled); + } + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/layout_inspector/LayoutInspector.java b/app/src/main/java/com/stardust/scriptdroid/layout_inspector/LayoutInspector.java index 8a190fa5..46bd0c5f 100644 --- a/app/src/main/java/com/stardust/scriptdroid/layout_inspector/LayoutInspector.java +++ b/app/src/main/java/com/stardust/scriptdroid/layout_inspector/LayoutInspector.java @@ -4,6 +4,7 @@ import android.accessibilityservice.AccessibilityService; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; +import com.stardust.scriptdroid.service.AccessibilityWatchDogService; import com.stardust.view.accessibility.AccessibilityDelegate; /** @@ -12,31 +13,30 @@ import com.stardust.view.accessibility.AccessibilityDelegate; public class LayoutInspector implements AccessibilityDelegate { - private AccessibilityNodeInfo mRootInActiveWindow; private NodeInfo mCapture; @Override public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) { - mRootInActiveWindow = service.getRootInActiveWindow(); return false; } - public AccessibilityNodeInfo getRootInActiveWindow() { - return mRootInActiveWindow; - } - public NodeInfo captureCurrentWindow() { - if (mRootInActiveWindow == null) { + AccessibilityService service = AccessibilityWatchDogService.getInstance(); + if (service == null) { mCapture = null; } else { - mCapture = NodeInfo.capture(mRootInActiveWindow); + AccessibilityNodeInfo root = service.getRootInActiveWindow(); + if (root == null) { + mCapture = null; + } else { + mCapture = NodeInfo.capture(root); + } } return mCapture; } public void clearCapture() { - mRootInActiveWindow = null; mCapture = null; } diff --git a/app/src/main/java/com/stardust/scriptdroid/layout_inspector/NodeInfo.java b/app/src/main/java/com/stardust/scriptdroid/layout_inspector/NodeInfo.java index 8a605717..bfdd8962 100644 --- a/app/src/main/java/com/stardust/scriptdroid/layout_inspector/NodeInfo.java +++ b/app/src/main/java/com/stardust/scriptdroid/layout_inspector/NodeInfo.java @@ -1,17 +1,20 @@ package com.stardust.scriptdroid.layout_inspector; import android.graphics.Rect; +import android.support.annotation.Keep; import android.support.annotation.NonNull; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.view.accessibility.AccessibilityNodeInfo; +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; + import java.util.ArrayList; import java.util.List; /** * Created by Stardust on 2017/3/10. */ - +@Keep public class NodeInfo { private List children = new ArrayList<>(); @@ -74,7 +77,7 @@ public class NodeInfo { public static NodeInfo capture(@NonNull AccessibilityNodeInfo root) { NodeInfo nodeInfo = new NodeInfo(root); for (int i = 0; i < root.getChildCount(); i++) { - AccessibilityNodeInfo child = root.getChild(i); + AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(root, i); if (child != null) { nodeInfo.children.add(capture(child)); } diff --git a/app/src/main/java/com/stardust/scriptdroid/record/accessibility/AccessibilityActionConverter.java b/app/src/main/java/com/stardust/scriptdroid/record/accessibility/AccessibilityActionConverter.java index dcdc0fe1..6a428b47 100644 --- a/app/src/main/java/com/stardust/scriptdroid/record/accessibility/AccessibilityActionConverter.java +++ b/app/src/main/java/com/stardust/scriptdroid/record/accessibility/AccessibilityActionConverter.java @@ -6,7 +6,7 @@ import android.util.SparseArray; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; -import com.stardust.autojs.runtime.action.FilterAction; +import com.stardust.automator.simple_action.FilterAction; import com.stardust.scriptdroid.layout_inspector.NodeInfo; import com.stardust.util.SparseArrayEntries; import com.stardust.view.accessibility.AccessibilityNodeInfoHelper; diff --git a/app/src/main/java/com/stardust/scriptdroid/record/accessibility/AccessibilityActionRecorder.java b/app/src/main/java/com/stardust/scriptdroid/record/accessibility/AccessibilityActionRecorder.java index a1683e43..a92635f0 100644 --- a/app/src/main/java/com/stardust/scriptdroid/record/accessibility/AccessibilityActionRecorder.java +++ b/app/src/main/java/com/stardust/scriptdroid/record/accessibility/AccessibilityActionRecorder.java @@ -2,6 +2,7 @@ package com.stardust.scriptdroid.record.accessibility; import android.accessibilityservice.AccessibilityService; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; import com.stardust.scriptdroid.record.Recorder; import com.stardust.view.accessibility.AccessibilityDelegate; diff --git a/app/src/main/java/com/stardust/scriptdroid/scripts/ScriptFile.java b/app/src/main/java/com/stardust/scriptdroid/scripts/ScriptFile.java index 2616a379..6c69b294 100644 --- a/app/src/main/java/com/stardust/scriptdroid/scripts/ScriptFile.java +++ b/app/src/main/java/com/stardust/scriptdroid/scripts/ScriptFile.java @@ -3,6 +3,7 @@ package com.stardust.scriptdroid.scripts; import android.os.Environment; import com.stardust.pio.PFile; + import java.io.File; import java.io.FileFilter; import java.io.FilenameFilter; @@ -56,7 +57,7 @@ public class ScriptFile extends File { return listFiles(new FileFilter() { @Override public boolean accept(File file) { - return file.isDirectory() || (file.getName().endsWith(".js") && !file.getName().startsWith(".")); + return (file.isDirectory() || file.getName().endsWith(".js")) && !file.getName().startsWith("."); } }); } diff --git a/app/src/main/java/com/stardust/scriptdroid/service/AccessibilityWatchDogService.java b/app/src/main/java/com/stardust/scriptdroid/service/AccessibilityWatchDogService.java index 88dc1b27..4f31bcc5 100644 --- a/app/src/main/java/com/stardust/scriptdroid/service/AccessibilityWatchDogService.java +++ b/app/src/main/java/com/stardust/scriptdroid/service/AccessibilityWatchDogService.java @@ -14,6 +14,8 @@ import java.lang.ref.WeakReference; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; /** * Created by Stardust on 2017/2/14. @@ -25,45 +27,10 @@ public class AccessibilityWatchDogService extends AccessibilityService { private static final SortedMap mDelegates = new TreeMap<>(); private static WeakReference instance; + private Executor mExecutor = Executors.newSingleThreadExecutor(); - public static void addDelegate(AccessibilityDelegate delegate, int uniquePriority) { - synchronized (mDelegates) { - mDelegates.put(uniquePriority, delegate); - } - } - - public static boolean containsPriority(int priority) { - synchronized (mDelegates) { - return mDelegates.containsKey(priority); - } - } - - public static AccessibilityDelegate getDelegate(int priority) { - synchronized (mDelegates) { - return mDelegates.get(priority); - } - } - - public static void addDelegateIfNeeded(int priority, Class delegateClass) { - try { - addDelegateIfNeeded(priority, delegateClass.newInstance()); - } catch (InstantiationException | IllegalAccessException e) { - throw new RuntimeException(e); - } - } - - public static void addDelegateIfNeeded(int priority, AccessibilityDelegate delegate) { - synchronized (mDelegates) { - if (!mDelegates.containsKey(priority)) { - mDelegates.put(priority, delegate); - } - } - } - - public static boolean removeDelegate(int priority) { - synchronized (mDelegates) { - return mDelegates.remove(priority) != null; - } + public static void addDelegate(int uniquePriority, AccessibilityDelegate delegate) { + mDelegates.put(uniquePriority, delegate); } public static boolean isEnable() { @@ -77,15 +44,18 @@ public class AccessibilityWatchDogService extends AccessibilityService { } @Override - public void onAccessibilityEvent(AccessibilityEvent event) { + public void onAccessibilityEvent(final AccessibilityEvent event) { Log.v(TAG, "onAccessibilityEvent: " + event); - synchronized (mDelegates) { - for (Map.Entry entry : mDelegates.entrySet()) { - //Log.v(TAG, "delegate: " + entry.getValue().getClass().getName()); - if (entry.getValue().onAccessibilityEvent(this, event)) - break; + mExecutor.execute(new Runnable() { + @Override + public void run() { + for (Map.Entry entry : mDelegates.entrySet()) { + Log.v(TAG, "delegate: " + entry.getValue().getClass().getName()); + if (entry.getValue().onAccessibilityEvent(AccessibilityWatchDogService.this, event)) + break; + } } - } + }); } @Override diff --git a/app/src/main/java/com/stardust/scriptdroid/service/VolumeChangeObverseService.java b/app/src/main/java/com/stardust/scriptdroid/service/VolumeChangeObverseService.java deleted file mode 100644 index d3f19fa2..00000000 --- a/app/src/main/java/com/stardust/scriptdroid/service/VolumeChangeObverseService.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.stardust.scriptdroid.service; - -import android.app.Service; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.os.IBinder; -import android.support.annotation.Nullable; -import android.support.v7.widget.SwitchCompat; - -import com.stardust.scriptdroid.record.Recorder; -import com.stardust.scriptdroid.record.accessibility.AccessibilityActionRecorder; -import com.stardust.scriptdroid.Pref; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.List; - - -/** - * Created by Stardust on 2017/2/16. - */ - -public class VolumeChangeObverseService extends Service { - - public interface OnVolumeChangeListener { - - void onVolumeChange(); - } - - private static final String ACTION_VOLUME_CHANGE = "android.media.VOLUME_CHANGED_ACTION"; - private static List mOnVolumeChangeListenerList = new ArrayList<>(); - - public static void addOnVolumeChangeListener(OnVolumeChangeListener listener) { - mOnVolumeChangeListenerList.add(listener); - } - - public static void removeOnVolumeChangeListener(OnVolumeChangeListener listener) { - mOnVolumeChangeListenerList.remove(listener); - } - - private VolumeChangeReceiver mVolumeChangeReceiver; - - @Override - public void onCreate() { - super.onCreate(); - mVolumeChangeReceiver = new VolumeChangeReceiver(); - registerReceiver(mVolumeChangeReceiver, new IntentFilter(ACTION_VOLUME_CHANGE)); - } - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public void onDestroy() { - super.onDestroy(); - unregisterReceiver(mVolumeChangeReceiver); - } - - private class VolumeChangeReceiver extends BroadcastReceiver { - - private long mLastChangeMillis; - - @Override - public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(ACTION_VOLUME_CHANGE)) { - if (System.currentTimeMillis() - mLastChangeMillis < 400) { - return; - } - mLastChangeMillis = System.currentTimeMillis(); - for (OnVolumeChangeListener listener : mOnVolumeChangeListenerList) { - listener.onVolumeChange(); - } - } - } - } - -} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/BaseActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/BaseActivity.java index ca302f32..e8e25fd7 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/BaseActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/BaseActivity.java @@ -1,5 +1,6 @@ package com.stardust.scriptdroid.ui; +import android.app.Activity; import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; @@ -10,6 +11,7 @@ import android.view.View; import com.stardust.scriptdroid.R; +import com.stardust.scriptdroid.external.tasker.TaskPrefEditActivity; import com.stardust.theme.ThemeColorManager; import com.stardust.util.BackPressedHandler; @@ -71,18 +73,7 @@ public abstract class BaseActivity extends AppCompatActivity { } public void setToolbarAsBack(String title) { - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - toolbar.setTitle(title); - setSupportActionBar(toolbar); - if (getSupportActionBar() != null) { - toolbar.setNavigationOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finish(); - } - }); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - } + setToolbarAsBack(this, R.id.toolbar, title); } public void registerBackPressedHandler(BackPressedHandler handler) { @@ -95,4 +86,19 @@ public abstract class BaseActivity extends AppCompatActivity { super.onBackPressed(); } } + + public static void setToolbarAsBack(final AppCompatActivity activity, int id, String title) { + Toolbar toolbar = (Toolbar) activity.findViewById(id); + toolbar.setTitle(title); + activity.setSupportActionBar(toolbar); + if (activity.getSupportActionBar() != null) { + toolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + activity.finish(); + } + }); + activity.getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + } } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditActivity.java index 868e3cac..8b63157d 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/EditActivity.java @@ -1,5 +1,6 @@ package com.stardust.scriptdroid.ui.edit; +import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -9,9 +10,9 @@ import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; -import android.support.v7.widget.Toolbar; import android.util.SparseArray; import android.view.View; +import android.view.inputmethod.InputMethod; import com.afollestad.materialdialogs.DialogAction; import com.afollestad.materialdialogs.MaterialDialog; @@ -19,11 +20,14 @@ import com.stardust.autojs.engine.JavaScriptEngine; import com.stardust.autojs.ScriptExecutionListener; import com.stardust.autojs.script.FileScriptSource; import com.stardust.autojs.script.ScriptSource; +import com.stardust.autojs.script.StringScriptSource; import com.stardust.pio.PFile; import com.stardust.scriptdroid.Pref; import com.stardust.scriptdroid.autojs.AutoJs; import com.stardust.scriptdroid.scripts.ScriptFile; +import com.stardust.scriptdroid.ui.BaseActivity; import com.stardust.scriptdroid.ui.edit.editor920.Editor920Activity; +import com.stardust.scriptdroid.ui.edit.editor920.Editor920Utils; import com.stardust.scriptdroid.ui.edit.sidemenu.EditSideMenuFragment; import com.stardust.scriptdroid.ui.edit.sidemenu.FunctionListRecyclerView; import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; @@ -52,11 +56,40 @@ import timber.log.Timber; public class EditActivity extends Editor920Activity { + public static class InputMethodEnhanceBarBridge implements InputMethodEnhanceBar.EditTextBridge { + + private Editor920Activity mEditor920Activity; + private TextView mTextView; + + public InputMethodEnhanceBarBridge(Editor920Activity editor920Activity, TextView textView) { + mEditor920Activity = editor920Activity; + mTextView = textView; + } + + @Override + public void appendText(CharSequence text) { + mEditor920Activity.insertText(text); + } + + @Override + public void backspace(int count) { + + } + + @Override + public TextView getEditText() { + return mTextView; + } + } + + public static final String EXTRA_CONTENT = "Still Love Eating 17.4.5"; + private static final String KEY_EDIT_ACTIVITY_FIRST_USE = "KEY_EDIT_ACTIVITY_FIRST_USE"; private static final String ACTION_ON_RUN_FINISHED = "ACTION_ON_RUN_FINISHED"; private static final String EXTRA_EXCEPTION_MESSAGE = "EXTRA_EXCEPTION_MESSAGE"; + private static final ScriptExecutionListener SCRIPT_EXECUTION_LISTENER = new ScriptExecutionListener() { @Override @@ -82,12 +115,11 @@ public class EditActivity extends Editor920Activity { editFile(context, null, path); } - - public static void editAssetFile(Context context, String name, String path) { + public static void view(Context context, String name, String content) { context.startActivity(new Intent(context, EditActivity.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra("fromAssets", true) - .putExtra("path", path) + .putExtra("readOnly", true) + .putExtra("content", content) .putExtra("name", name)); } @@ -125,7 +157,10 @@ public class EditActivity extends Editor920Activity { public void onCreate(Bundle b) { super.onCreate(b); - handleIntent(); + setTheme(R.style.EditorTheme); + mView = View.inflate(this, R.layout.activity_edit, null); + setContentView(mView); + handleIntent(getIntent()); setUpUI(); setUpEditor(); registerReceiver(mOnRunFinishedReceiver, new IntentFilter(ACTION_ON_RUN_FINISHED)); @@ -149,32 +184,29 @@ public class EditActivity extends Editor920Activity { } } - private void handleIntent() { - String path = getIntent().getStringExtra("path"); - mName = getIntent().getStringExtra("name"); - if (path == null) { - finish(); + private void handleIntent(Intent intent) { + String path = intent.getStringExtra("path"); + mName = intent.getStringExtra("name"); + mReadOnly = intent.getBooleanExtra("readOnly", false); + boolean saveEnabled = intent.getBooleanExtra("saveEnabled", true); + if (mReadOnly || !saveEnabled) { + findViewById(R.id.save).setVisibility(View.GONE); + } + String content = intent.getStringExtra("content"); + if (content != null) { + mEditorDelegate = new EditorDelegate(0, mName, content); } else { - if (getIntent().getBooleanExtra("fromAssets", false)) { - mReadOnly = true; - //TODO 优化 - mFile = PFile.copyAssetToTmpFile(this, path); - } else { - mFile = new File(path); - } + mFile = new File(path); if (mName == null) { mName = mFile.getName(); } + mEditorDelegate = new EditorDelegate(0, mFile, 0, "utf-8"); } - } private void setUpUI() { - setTheme(R.style.EditorTheme); ThemeColorManager.addActivityStatusBar(this); - mView = View.inflate(this, R.layout.activity_edit, null); mDrawerLayout = (DrawerLayout) mView.findViewById(R.id.drawer_layout); - setContentView(mView); initSideMenuFragment(); setUpToolbar(); initMenuItem(); @@ -194,57 +226,33 @@ public class EditActivity extends Editor920Activity { } private void setUpEditor() { - if (mFile != null) { - mEditorDelegate = new EditorDelegate(0, mFile, 0, null); - final EditorView editorView = (EditorView) findViewById(R.id.editor); - mEditorDelegate.setEditorView(editorView); - editorView.getEditText().setReadOnly(mReadOnly); - editorView.getEditText().setHorizontallyScrolling(true); - InputMethodEnhanceBar inputMethodEnhanceBar = (InputMethodEnhanceBar) findViewById(R.id.input_method_enhance_bar); - if (mReadOnly) { - inputMethodEnhanceBar.setVisibility(View.GONE); - } else { - inputMethodEnhanceBar.setEditTextBridge(new InputMethodEnhanceBar.EditTextBridge() { - @Override - public void appendText(CharSequence text) { - insertText(text); - } + final EditorView editorView = (EditorView) findViewById(R.id.editor); + mEditorDelegate.setEditorView(editorView); + if (mFile == null) + Editor920Utils.setLang(mEditorDelegate, "JavaScript"); + editorView.getEditText().setReadOnly(mReadOnly); + editorView.getEditText().setHorizontallyScrolling(true); + setUpInputMethodEnhanceBar(editorView); - @Override - public void backspace(int count) { + } - } - - @Override - public TextView getEditText() { - return editorView.getEditText(); - } - - - }); - } + private void setUpInputMethodEnhanceBar(final EditorView editorView) { + InputMethodEnhanceBar inputMethodEnhanceBar = (InputMethodEnhanceBar) findViewById(R.id.input_method_enhance_bar); + if (mReadOnly) { + inputMethodEnhanceBar.setVisibility(View.GONE); + } else { + inputMethodEnhanceBar.setEditTextBridge(new InputMethodEnhanceBarBridge(this, editorView.getEditText())); } } private void setUpToolbar() { - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - toolbar.setTitle(mName); - setSupportActionBar(toolbar); - if (getSupportActionBar() != null) { - toolbar.setNavigationOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - finish(); - } - }); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - } + BaseActivity.setToolbarAsBack(this, R.id.toolbar, mName); } @ViewBinding.Click(R.id.run) private void runAndSaveFileIFNeeded() { - if (mEditorDelegate.isChanged()) { + if (!mReadOnly && mEditorDelegate.isChanged()) { saveFile(false, new SaveListener() { @Override public void onSaved() { @@ -267,7 +275,11 @@ public class EditActivity extends Editor920Activity { private void run() { Snackbar.make(mView, R.string.text_start_running, Snackbar.LENGTH_SHORT).show(); setMenuStatus(R.id.run, MenuDef.STATUS_DISABLED); - AutoJs.getInstance().getScriptEngineService().execute(new FileScriptSource(mName, mFile), SCRIPT_EXECUTION_LISTENER); + if (mFile != null) { + AutoJs.getInstance().getScriptEngineService().execute(new FileScriptSource(mName, mFile), SCRIPT_EXECUTION_LISTENER); + } else { + AutoJs.getInstance().getScriptEngineService().execute(new StringScriptSource(mName, mEditorDelegate.getText()), SCRIPT_EXECUTION_LISTENER); + } } @ViewBinding.Click(R.id.undo) @@ -314,6 +326,7 @@ public class EditActivity extends Editor920Activity { } } + private void showExitConfirmDialog() { new ThemeColorMaterialDialogBuilder(this) .title(R.string.text_alert) diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor920/Editor920Utils.java b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor920/Editor920Utils.java new file mode 100644 index 00000000..9429bf86 --- /dev/null +++ b/app/src/main/java/com/stardust/scriptdroid/ui/edit/editor920/Editor920Utils.java @@ -0,0 +1,27 @@ +package com.stardust.scriptdroid.ui.edit.editor920; + +import com.jecelyin.editor.v2.ui.Document; +import com.jecelyin.editor.v2.ui.EditorDelegate; + +import java.lang.reflect.Field; + +/** + * Created by Stardust on 2017/4/5. + */ + +public class Editor920Utils { + + public static Document getDocument(EditorDelegate editorDelegate) { + try { + Field field = EditorDelegate.class.getDeclaredField("document"); + field.setAccessible(true); + return (Document) field.get(editorDelegate); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public static void setLang(EditorDelegate editorDelegate, String name) { + getDocument(editorDelegate).setMode(name); + } +} diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/error/IssueReporterActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/error/IssueReporterActivity.java index 21aadca3..6e5e173f 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/error/IssueReporterActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/error/IssueReporterActivity.java @@ -15,13 +15,13 @@ public class IssueReporterActivity extends AbstractIssueReporterActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //绕过GitHub安全检查 - String token = decode("MzA5MWJhNWI0NDM2NWYzOGRmNDA4ZWRhM2Y5MTUxN2ZlNDVlZDBhZQ=="); + String token = decode("ZGRjYWM4NTI5ZmJjZjFkMjk3NTU1NWYyODEwNmQzYjJhYWU0MjFmMA=="); setGuestToken(token); } @Override protected GithubTarget getTarget() { - return new GithubTarget("hyb1996", "NoRootScriptDroid"); + return new GithubTarget("hyb1996-guest", "AutoJsIssueReport"); } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/operation/ScriptFileOperation.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/operation/ScriptFileOperation.java index 3a5ad523..b2ae830f 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/operation/ScriptFileOperation.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/operation/ScriptFileOperation.java @@ -43,7 +43,7 @@ public abstract class ScriptFileOperation { public static void createShortcut(ScriptFile scriptFile) { new Shortcut(App.getApp()).name(scriptFile.getSimplifiedName()) .targetClass(ShortcutActivity.class) - .icon(R.drawable.ic_robot_green) + .icon(R.drawable.ic_node_js_black) .extras(new Intent().putExtra(CommonUtils.EXTRA_KEY_PATH, scriptFile.getPath())) .send(); } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/sample_list/SampleScriptListFragment.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/sample_list/SampleScriptListFragment.java index ccfd4ada..184bc780 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/sample_list/SampleScriptListFragment.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/sample_list/SampleScriptListFragment.java @@ -9,11 +9,13 @@ import android.view.ViewGroup; import com.afollestad.materialdialogs.MaterialDialog; import com.stardust.app.Fragment; +import com.stardust.pio.PFile; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.scripts.sample.Sample; import com.stardust.scriptdroid.scripts.sample.SampleFileManager; import com.stardust.scriptdroid.ui.edit.EditActivity; import com.stardust.scriptdroid.ui.main.MainActivity; +import com.stardust.util.AssetsCache; import java.io.IOException; @@ -75,7 +77,7 @@ public class SampleScriptListFragment extends Fragment { } private void viewSample(Sample sample) { - EditActivity.editAssetFile(getContext(), sample.name, sample.path); + EditActivity.view(getContext(), sample.name, AssetsCache.get(getActivity(), sample.path)); } } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/script_list/ScriptAndFolderListRecyclerView.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/script_list/ScriptAndFolderListRecyclerView.java index 7ddec7af..6e07d7cc 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/script_list/ScriptAndFolderListRecyclerView.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/script_list/ScriptAndFolderListRecyclerView.java @@ -110,8 +110,8 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView { init(); } - private void setCurrentDirectory(final ScriptFile folder, boolean canGoBack) { - mCurrentDirectory = folder; + private void setCurrentDirectory(final ScriptFile directory, boolean canGoBack) { + mCurrentDirectory = directory; mCanGoBack = canGoBack; if (mFileProcessListener != null) { mFileProcessListener.onFilesListing(); @@ -119,7 +119,7 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView { new Thread(new Runnable() { @Override public void run() { - mScriptFileList = mStorageScriptProvider.getDirectoryScriptFiles(folder); + mScriptFileList = mStorageScriptProvider.getDirectoryScriptFiles(directory); post(new Runnable() { @Override public void run() { @@ -131,6 +131,10 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView { }).start(); } + public void setCurrentDirectory(final ScriptFile directory) { + setCurrentDirectory(directory, !directory.equals(mRootDirectory)); + } + private void setRootDirectory(ScriptFile folder) { mRootDirectory = folder; setCurrentDirectory(mRootDirectory, false); @@ -195,7 +199,7 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView { Bundle bundle = (Bundle) state; mRootDirectory = (ScriptFile) bundle.getSerializable("root"); mCurrentDirectory = (ScriptFile) bundle.getSerializable("current"); - setCurrentDirectory(mCurrentDirectory, !mCurrentDirectory.equals(mRootDirectory)); + setCurrentDirectory(mCurrentDirectory); super.onRestoreInstanceState(bundle.getParcelable("superData")); } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/script_list/ScriptListWithProgressBarView.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/script_list/ScriptListWithProgressBarView.java index dc4cdddd..0c200f45 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/script_list/ScriptListWithProgressBarView.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/script_list/ScriptListWithProgressBarView.java @@ -12,6 +12,7 @@ import android.view.View; import android.widget.FrameLayout; import com.stardust.scriptdroid.R; +import com.stardust.scriptdroid.scripts.ScriptFile; import com.stardust.scriptdroid.scripts.StorageScriptProvider; /** @@ -89,4 +90,7 @@ public class ScriptListWithProgressBarView extends FrameLayout { } + public void setCurrentDirectory(ScriptFile directory) { + mScriptAndFolderListRecyclerView.setCurrentDirectory(directory); + } } diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/task/TaskListRecyclerView.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/task/TaskListRecyclerView.java index 31a94a4d..7e6a0ec7 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/task/TaskListRecyclerView.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/task/TaskListRecyclerView.java @@ -19,6 +19,7 @@ import com.stardust.autojs.script.ScriptSource; import com.stardust.scriptdroid.R; import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; +import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -44,7 +45,7 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView implements Java } }; - private final List mScriptEngines = new LinkedList<>(); + private final List mScriptEngines = new ArrayList<>(); private Adapter mAdapter; public TaskListRecyclerView(Context context) { diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/main/task/TaskManagerFragment.java b/app/src/main/java/com/stardust/scriptdroid/ui/main/task/TaskManagerFragment.java index 0a4e8073..768b23d0 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/main/task/TaskManagerFragment.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/main/task/TaskManagerFragment.java @@ -45,9 +45,14 @@ public class TaskManagerFragment extends Fragment { @Override public void onSomethingChanged() { - boolean noRunningScript = mTaskListRecyclerView.getAdapter().getItemCount() == 0; + final boolean noRunningScript = mTaskListRecyclerView.getAdapter().getItemCount() == 0; mNoRunningScriptNotice.setVisibility(noRunningScript ? View.VISIBLE : View.GONE); - mCloseAllView.setVisibility(noRunningScript ? View.GONE : View.VISIBLE); + mCloseAllView.postDelayed(new Runnable() { + @Override + public void run() { + mCloseAllView.setVisibility(noRunningScript ? View.GONE : View.VISIBLE); + } + }, 150); } }); diff --git a/app/src/main/java/com/stardust/scriptdroid/ui/settings/AboutActivity.java b/app/src/main/java/com/stardust/scriptdroid/ui/settings/AboutActivity.java index 9f320efa..020ffd24 100644 --- a/app/src/main/java/com/stardust/scriptdroid/ui/settings/AboutActivity.java +++ b/app/src/main/java/com/stardust/scriptdroid/ui/settings/AboutActivity.java @@ -118,10 +118,5 @@ public class AboutActivity extends BaseActivity { Toast.makeText(this, R.string.text_it_is_the_developer_of_app, Toast.LENGTH_LONG).show(); } - private void setClipText(CharSequence label, CharSequence text) { - ClipboardManager clipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); - clipboardManager.setPrimaryClip(ClipData.newPlainText(label, text)); - Toast.makeText(this, label + getString(R.string.text_already_copy_to_clip), Toast.LENGTH_SHORT).show(); - } } diff --git a/app/src/main/res/drawable/ic_refresh_white_24dp.png b/app/src/main/res/drawable/ic_refresh_white_24dp.png new file mode 100644 index 00000000..1989184b Binary files /dev/null and b/app/src/main/res/drawable/ic_refresh_white_24dp.png differ diff --git a/app/src/main/res/layout/activity_tasker_edit.xml b/app/src/main/res/layout/activity_tasker_edit.xml index 9f7adfe0..b346515a 100644 --- a/app/src/main/res/layout/activity_tasker_edit.xml +++ b/app/src/main/res/layout/activity_tasker_edit.xml @@ -1,9 +1,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/floating_script_list_recycler_view_item.xml b/app/src/main/res/layout/floating_script_list_recycler_view_item.xml index 7af2383b..57d11d2c 100644 --- a/app/src/main/res/layout/floating_script_list_recycler_view_item.xml +++ b/app/src/main/res/layout/floating_script_list_recycler_view_item.xml @@ -7,7 +7,8 @@ android:orientation="vertical" android:paddingBottom="12dp" android:paddingLeft="16dp" - android:paddingTop="12dp"> + android:paddingTop="12dp" + tools:background="@color/colorPrimary"> + + + diff --git a/app/src/main/res/menu/tasker_script_edit_menu.xml b/app/src/main/res/menu/tasker_script_edit_menu.xml new file mode 100644 index 00000000..153a7483 --- /dev/null +++ b/app/src/main/res/menu/tasker_script_edit_menu.xml @@ -0,0 +1,24 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1db8ee04..8033489d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -188,6 +188,12 @@ 请选择要导入的脚本 刷新 ω<*)]]> + 请选择脚本 + 编辑预执行脚本 + 预执行脚本 + 清空预执行脚本 + 重置脚本文件选择 + 将在脚本文件执行前执行 diff --git a/app/src/main/res/values/theme_editor.xml b/app/src/main/res/values/theme_editor.xml index 8451d599..a28ecf08 100644 --- a/app/src/main/res/values/theme_editor.xml +++ b/app/src/main/res/values/theme_editor.xml @@ -48,7 +48,7 @@ #000000 #FFFFFF #000000 - #4D97FF54 + @color/colorPrimary #dddddd #000000 #dfdfdf diff --git a/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java b/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java index 7b78c3bc..9dae6380 100644 --- a/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java +++ b/app/src/test/java/com/stardust/scriptdroid/ExampleUnitTest.java @@ -1,5 +1,6 @@ package com.stardust.scriptdroid; +import com.jecelyin.common.http.Base64; import com.stardust.util.LimitedHashMap; import org.junit.Test; diff --git a/autojs/build.gradle b/autojs/build.gradle index 209fead1..0e6e69d8 100644 --- a/autojs/build.gradle +++ b/autojs/build.gradle @@ -19,6 +19,10 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + + packagingOptions { + exclude 'org/mozilla/javascript/tools/debugger/build.xml' + } } dependencies { @@ -28,7 +32,7 @@ dependencies { }) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:25.3.0' - compile 'com.github.hyb1996:node-android-lib:1.0.13' + compile 'com.github.hyb1996:node-android-lib:1.0.14' compile project(path: ':common') compile project(path: ':automator') } diff --git a/autojs/src/main/assets/javascript_engine_init.js b/autojs/src/main/assets/javascript_engine_init.js index e3654ecf..866e6101 100644 --- a/autojs/src/main/assets/javascript_engine_init.js +++ b/autojs/src/main/assets/javascript_engine_init.js @@ -8,6 +8,9 @@ if(__engine__ == "rhino"){ __importClassOld__(pack); } } + var loadJar = function(path){ + __runtime__.loadJar(path); + } } var toast = function(text){ diff --git a/autojs/src/main/java/com/stardust/autojs/engine/JavaScriptEngineManager.java b/autojs/src/main/java/com/stardust/autojs/engine/JavaScriptEngineManager.java index f623713e..8eb19d15 100644 --- a/autojs/src/main/java/com/stardust/autojs/engine/JavaScriptEngineManager.java +++ b/autojs/src/main/java/com/stardust/autojs/engine/JavaScriptEngineManager.java @@ -30,7 +30,6 @@ public abstract class JavaScriptEngineManager { private Map mGlobalVariableMap = new HashMap<>(); private final Set mEngines = new HashSet<>(); - private boolean mIsStopping = false; private EngineLifecycleCallback mEngineLifecycleCallback; private final ScriptSource INIT_SCRIPT; @@ -82,8 +81,6 @@ public abstract class JavaScriptEngineManager { void removeEngine(JavaScriptEngine engine) { synchronized (mEngines) { - if (mIsStopping) - return; mEngines.remove(engine); if(mEngineLifecycleCallback != null){ mEngineLifecycleCallback.onEngineRemove(engine); @@ -111,16 +108,11 @@ public abstract class JavaScriptEngineManager { public int stopAll() { synchronized (mEngines) { - mIsStopping = true; int n = mEngines.size(); for (JavaScriptEngine engine : mEngines) { engine.forceStop(); - if(mEngineLifecycleCallback != null){ - mEngineLifecycleCallback.onEngineRemove(engine); - } } mEngines.clear(); - mIsStopping = false; return n; } } diff --git a/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.java b/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.java index f8d0e09e..9e8ec7ca 100644 --- a/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.java +++ b/autojs/src/main/java/com/stardust/autojs/engine/RhinoJavaScriptEngine.java @@ -1,5 +1,7 @@ package com.stardust.autojs.engine; +import com.stardust.autojs.rhino_android.AndroidContextFactory; +import com.stardust.autojs.rhino_android.RhinoAndroidHelper; import com.stardust.autojs.runtime.ScriptStopException; import com.stardust.autojs.script.ScriptSource; @@ -9,16 +11,14 @@ import org.mozilla.javascript.ImporterTopLevel; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; +import java.io.File; + /** * Created by Stardust on 2017/4/2. */ public class RhinoJavaScriptEngine implements JavaScriptEngine { - static { - ContextFactory.initGlobal(new InterruptibleContextFactory()); - } - private Context mContext; private Scriptable mScriptable; private Thread mThread; @@ -69,7 +69,6 @@ public class RhinoJavaScriptEngine implements JavaScriptEngine { mContext.evaluateString(mScriptable, mEngineManager.getInitScript().getScript(), "", 1, null); } - public Context getContext() { return mContext; } @@ -85,23 +84,32 @@ public class RhinoJavaScriptEngine implements JavaScriptEngine { } protected Context createContext() { - Context context = Context.enter(); + if (!ContextFactory.hasExplicitGlobal()) { + ContextFactory.initGlobal(new InterruptibleAndroidContextFactory(new File(mEngineManager.getContext().getCacheDir(), "classes"))); + } + Context context = new RhinoAndroidHelper(mEngineManager.getContext()).enterContext(); context.setOptimizationLevel(-1); context.setLanguageVersion(Context.VERSION_1_7); return context; } - private static class InterruptibleContextFactory extends ContextFactory { + private static class InterruptibleAndroidContextFactory extends AndroidContextFactory { + + public InterruptibleAndroidContextFactory(File cacheDirectory) { + super(cacheDirectory); + } @Override protected void observeInstructionCount(Context cx, int instructionCount) { if (Thread.currentThread().isInterrupted()) { + throw new ScriptStopException(new InterruptedException()); } } @Override protected Context makeContext() { + Context cx = super.makeContext(); cx.setInstructionObserverThreshold(10000); return cx; diff --git a/autojs/src/main/java/com/stardust/autojs/rhino_android/AndroidClassLoader.java b/autojs/src/main/java/com/stardust/autojs/rhino_android/AndroidClassLoader.java new file mode 100644 index 00000000..f6ceee7e --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/rhino_android/AndroidClassLoader.java @@ -0,0 +1,148 @@ +package com.stardust.autojs.rhino_android; + +import com.android.dx.command.dexer.Main; + +import net.lingala.zip4j.core.ZipFile; +import net.lingala.zip4j.exception.ZipException; +import net.lingala.zip4j.model.FileHeader; +import net.lingala.zip4j.model.ZipParameters; + +import org.mozilla.javascript.GeneratedClassLoader; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import dalvik.system.DexFile; + +/** + * Created by Stardust on 2017/4/5. + */ + +public class AndroidClassLoader extends ClassLoader implements GeneratedClassLoader { + + + private final ClassLoader parent; + private List dx; + private final File dexFile; + private final File odexOatFile; + private final File classFile; + + /** + * Create a new instance with the given parent classloader and cache dierctory + * + * @param parent the parent + * @param dir the cache directory + */ + public AndroidClassLoader(ClassLoader parent, File dir) { + this.parent = parent; + dx = new ArrayList<>(); + dexFile = new File(dir, "dex-" + hashCode() + ".jar"); + odexOatFile = new File(dir, "odex_oat-" + hashCode() + ".tmp"); + classFile = new File(dir, "class-" + hashCode() + ".jar"); + dir.mkdirs(); + } + + /** + * {@inheritDoc} + */ + @Override + public Class defineClass(String name, byte[] data) { + try { + final ZipFile zipFile = new ZipFile(classFile); + final ZipParameters parameters = new ZipParameters(); + parameters.setFileNameInZip(name.replace('.', '/') + ".class"); + parameters.setSourceExternalStream(true); + zipFile.addStream(new ByteArrayInputStream(data), parameters); + return dexJar().loadClass(name, parent); + } catch (IOException | ZipException e) { + throw new FatalLoadingException(e); + } finally { + dexFile.delete(); + odexOatFile.delete(); + } + } + + public void loadJar(File jar) throws IOException { + try { + final ZipFile zipFile = new ZipFile(classFile); + final ZipFile jarFile = new ZipFile(jar); + //noinspection unchecked + for (FileHeader header : (List) jarFile.getFileHeaders()) { + if (!header.isDirectory()) { + final ZipParameters parameters = new ZipParameters(); + parameters.setFileNameInZip(header.getFileName()); + parameters.setSourceExternalStream(true); + zipFile.addStream(jarFile.getInputStream(header), parameters); + } + } + dexJar(); + } catch (ZipException e) { + IOException exception = new IOException(); + //noinspection UnnecessaryInitCause + exception.initCause(e); + throw exception; + } + } + + private DexFile dexJar() throws IOException { + if (!classFile.exists()) { + classFile.createNewFile(); + } + final Main.Arguments arguments = new Main.Arguments(); + arguments.fileNames = new String[]{classFile.getPath()}; + arguments.outName = dexFile.getPath(); + arguments.jarOutput = true; + Main.run(arguments); + DexFile dex = DexFile.loadDex(dexFile.getPath(), odexOatFile.getPath(), 0); + dx.add(dex); + return dex; + } + + /** + * Does nothing + * + * @param aClass ignored + */ + @Override + public void linkClass(Class aClass) { + //doesn't make sense on android + } + + /** + * Try to load a class. This will search all defined classes, all loaded jars and the parent class loader. + * + * @param name the name of the class to load + * @param resolve ignored + * @return the class + * @throws ClassNotFoundException if the class could not be found in any of the locations + */ + @Override + public Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + Class loadedClass = findLoadedClass(name); + if (loadedClass == null) { + for (DexFile dex : dx) { + loadedClass = dex.loadClass(name, parent); + if (loadedClass != null) { + break; + } + } + if (loadedClass == null) { + loadedClass = parent.loadClass(name); + } + } + return loadedClass; + } + + /** + * Might be thrown in any Rhino method that loads bytecode if the loading failed + */ + public static class FatalLoadingException extends RuntimeException { + FatalLoadingException(Throwable t) { + super("Failed to define class", t); + } + } +} diff --git a/autojs/src/main/java/com/stardust/autojs/rhino_android/AndroidContextFactory.java b/autojs/src/main/java/com/stardust/autojs/rhino_android/AndroidContextFactory.java new file mode 100644 index 00000000..d2b0dfed --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/rhino_android/AndroidContextFactory.java @@ -0,0 +1,52 @@ +package com.stardust.autojs.rhino_android; + + +import com.stardust.autojs.runtime.ScriptStopException; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextFactory; + +import java.io.File; + +/** + * Created by Stardust on 2017/4/5. + */ + +public class AndroidContextFactory extends ContextFactory { + private final File cacheDirectory; + + /** + * Create a new factory. It will cache generated code in the given directory + * + * @param cacheDirectory the cache directory + */ + public AndroidContextFactory(File cacheDirectory) { + this.cacheDirectory = cacheDirectory; + initApplicationClassLoader(createClassLoader(com.faendir.rhino_android.AndroidContextFactory.class.getClassLoader())); + } + + /** + * Create a ClassLoader which is able to deal with bytecode + * + * @param parent the parent of the create classloader + * @return a new ClassLoader + */ + @Override + protected AndroidClassLoader createClassLoader(ClassLoader parent) { + return new AndroidClassLoader(parent, cacheDirectory); + } + + @Override + protected void observeInstructionCount(Context cx, int instructionCount) { + if (Thread.currentThread().isInterrupted()) { + throw new ScriptStopException(new InterruptedException()); + } + } + + @Override + protected Context makeContext() { + Context cx = super.makeContext(); + cx.setInstructionObserverThreshold(10000); + return cx; + } +} diff --git a/autojs/src/main/java/com/stardust/autojs/rhino_android/NoSecurityController.java b/autojs/src/main/java/com/stardust/autojs/rhino_android/NoSecurityController.java new file mode 100644 index 00000000..0e500a3d --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/rhino_android/NoSecurityController.java @@ -0,0 +1,23 @@ +package com.stardust.autojs.rhino_android; + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.GeneratedClassLoader; +import org.mozilla.javascript.SecurityController; + +import java.io.Serializable; + +/** + * Created by Stardust on 2017/4/5. + */ + +public class NoSecurityController extends SecurityController implements Serializable { + @Override + public GeneratedClassLoader createClassLoader(ClassLoader classLoader, Object o) { + return Context.getCurrentContext().createClassLoader(classLoader); + } + + @Override + public Object getDynamicSecurityDomain(Object o) { + return null; + } +} diff --git a/autojs/src/main/java/com/stardust/autojs/rhino_android/RhinoAndroidHelper.java b/autojs/src/main/java/com/stardust/autojs/rhino_android/RhinoAndroidHelper.java new file mode 100644 index 00000000..56e1434f --- /dev/null +++ b/autojs/src/main/java/com/stardust/autojs/rhino_android/RhinoAndroidHelper.java @@ -0,0 +1,93 @@ +package com.stardust.autojs.rhino_android; + +import android.support.annotation.VisibleForTesting; + + +import org.mozilla.javascript.Context; +import org.mozilla.javascript.ContextFactory; +import org.mozilla.javascript.SecurityController; + +import java.io.File; +import java.io.IOException; + +/** + * Created by Stardust on 2017/4/5. + */ + +public class RhinoAndroidHelper { + + private final File cacheDirectory; + + /** + * Constructs a new helper using the default temporary directory. + * Note: It is recommended to use a custom directory, so no permission problems occur. + */ + public RhinoAndroidHelper() { + this(new File(System.getProperty("java.io.tmpdir", "."), "classes")); + } + + /** + * Constructs a new helper using a directory in the applications cache. + * + * @param context any context + */ + public RhinoAndroidHelper(android.content.Context context) { + this(new File(context.getCacheDir(), "classes")); + } + + /** + * Constructs a helper using the specified directory as cache. + * + * @param cacheDirectory the cache directory to use + */ + public RhinoAndroidHelper(File cacheDirectory) { + this.cacheDirectory = cacheDirectory; + } + + /** + * call this instead of {@link Context#enter()} + * + * @return a context prepared for android + */ + public Context enterContext() { + if (!SecurityController.hasGlobal()) + SecurityController.initGlobal(new NoSecurityController()); + return getContextFactory().enterContext(); + } + + /** + * @return The Context factory which has to be used on android. + */ + @VisibleForTesting + public AndroidContextFactory getContextFactory() { + AndroidContextFactory factory; + if (!ContextFactory.hasExplicitGlobal()) { + factory = new AndroidContextFactory(cacheDirectory); + ContextFactory.getGlobalSetter().setContextFactoryGlobal(factory); + } else if (!(ContextFactory.getGlobal() instanceof AndroidContextFactory)) { + throw new IllegalStateException("Cannot initialize factory for Android Rhino: There is already another factory"); + } else { + factory = (AndroidContextFactory) ContextFactory.getGlobal(); + } + return factory; + } + + /** + * Compiles all classes in a jar file. They will be available in Rhino. + * + * @param jar the jar to load + * @throws IOException if the jar cannot be read or is invalid or there is a problem with the cache + */ + public void loadClassJar(File jar) throws IOException { + ((AndroidClassLoader) getContextFactory().getApplicationClassLoader()).loadJar(jar); + } + + /** + * @return a context prepared for android + * @deprecated use {@link #enterContext()} instead + */ + @Deprecated + public static Context prepareContext() { + return new RhinoAndroidHelper().enterContext(); + } +} diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/AccessibilityBridge.java b/autojs/src/main/java/com/stardust/autojs/runtime/AccessibilityBridge.java index 870d82bb..9c8235e9 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/AccessibilityBridge.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/AccessibilityBridge.java @@ -1,7 +1,10 @@ package com.stardust.autojs.runtime; -import com.stardust.autojs.runtime.action.ActionPerformAccessibilityDelegate; +import android.accessibilityservice.AccessibilityService; +import android.support.annotation.Nullable; + import com.stardust.automator.AccessibilityEventCommandHost; +import com.stardust.automator.simple_action.SimpleActionPerformHost; import com.stardust.view.accessibility.AccessibilityInfoProvider; /** @@ -16,7 +19,10 @@ public interface AccessibilityBridge { AccessibilityEventCommandHost getCommandHost(); - ActionPerformAccessibilityDelegate getActionPerformHost(); + SimpleActionPerformHost getActionPerformHost(); + + @Nullable + AccessibilityService getService(); } 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 8d9bc9e8..dc0387b8 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/ScriptRuntime.java @@ -6,15 +6,22 @@ import android.os.Handler; import android.widget.Toast; import com.stardust.autojs.R; -import com.stardust.autojs.runtime.action.ActionAutomator; +import com.stardust.autojs.rhino_android.AndroidClassLoader; +import com.stardust.autojs.runtime.simple_action.SimpleActionAutomator; import com.stardust.autojs.runtime.api.AppUtils; import com.stardust.autojs.runtime.api.Console; import com.stardust.autojs.runtime.api.UiSelector; +import com.stardust.pio.UncheckedIOException; import com.stardust.util.ClipboardUtil; import com.stardust.util.SdkVersionUtil; import com.stardust.util.Shell; import com.stardust.view.accessibility.AccessibilityInfoProvider; +import org.mozilla.javascript.ContextFactory; + +import java.io.File; +import java.io.IOException; + /** * Created by Stardust on 2017/1/27. @@ -35,7 +42,7 @@ public class ScriptRuntime { public Console console; @JavascriptField - public ActionAutomator automator; + public SimpleActionAutomator automator; @JavascriptField public AccessibilityInfoProvider info; @@ -47,7 +54,7 @@ public class ScriptRuntime { app = new AppUtils(context); info = accessibilityBridge.getInfoProvider(); this.console = console; - automator = new ActionAutomator(accessibilityBridge, this); + automator = new SimpleActionAutomator(accessibilityBridge, this); } @JavascriptInterface @@ -101,6 +108,15 @@ public class ScriptRuntime { } } + @JavascriptInterface + public void loadJar(String path) { + try { + ((AndroidClassLoader) ContextFactory.getGlobal().getApplicationClassLoader()).loadJar(new File(path)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + @JavascriptInterface public void stop() { Thread.interrupted(); diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/ActionPerformAccessibilityDelegate.java b/autojs/src/main/java/com/stardust/autojs/runtime/action/ActionPerformAccessibilityDelegate.java deleted file mode 100644 index 2b6bf779..00000000 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/ActionPerformAccessibilityDelegate.java +++ /dev/null @@ -1,77 +0,0 @@ -package com.stardust.autojs.runtime.action; - -import android.accessibilityservice.AccessibilityService; -import android.util.Log; -import android.view.accessibility.AccessibilityEvent; -import android.view.accessibility.AccessibilityNodeInfo; - -import com.stardust.view.accessibility.AccessibilityDelegate; - -import java.util.LinkedList; -import java.util.Queue; - - -/** - * Created by Stardust on 2017/1/21. - */ - -public class ActionPerformAccessibilityDelegate implements AccessibilityDelegate { - - private static final String TAG = "ActionPerformDelegate"; - - private final Queue mActions = new LinkedList<>(); - private Action mCurrentAction; - - public void addAction(Action action) { - synchronized (mActions) { - mActions.offer(action); - } - } - - @Override - public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) { - synchronized (mActions) { - mCurrentAction = getCurrentAction(); - if (mCurrentAction == null) - return false; - AccessibilityNodeInfo root = service.getRootInActiveWindow(); - if (root == null) { - return false; - } - performAction(root, mCurrentAction); - return false; - } - } - - private Action getCurrentAction() { - if (isCurrentActionValid()) { - return mCurrentAction; - } - if (mActions.isEmpty()) - return null; - return mActions.poll(); - } - - private boolean isCurrentActionValid() { - return mCurrentAction != null && mCurrentAction.isValid(); - } - - private void performAction(final AccessibilityNodeInfo root, final Action action) { - Log.i(TAG, "perform action:" + action); - if (action.perform(root)) { - action.setResult(true); - onActionPerformed(action); - } - } - - - private void onActionPerformed(Action action) { - mCurrentAction = null; - synchronized (action) { - action.notify(); - } - } - - -} - diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/SearchUpTargetAction.java b/autojs/src/main/java/com/stardust/autojs/runtime/action/SearchUpTargetAction.java deleted file mode 100644 index c219eb26..00000000 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/SearchUpTargetAction.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.stardust.autojs.runtime.action; - -import android.view.accessibility.AccessibilityNodeInfo; - -/** - * Created by Stardust on 2017/1/27. - */ - -public class SearchUpTargetAction extends SearchTargetAction { - - private Able mAble; - - public SearchUpTargetAction(int action, Filter filter) { - super(action, filter); - mAble = Able.ABLE_MAP.get(action); - } - - @Override - public AccessibilityNodeInfo searchTarget(AccessibilityNodeInfo n) { - AccessibilityNodeInfo node = n; - while (node != null && !mAble.isAble(node)) { - AccessibilityNodeInfo parent = node.getParent(); - if (node != n) { - node.recycle(); - } - node = parent; - } - return node; - } -} diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/AppUtils.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/AppUtils.java index d08d8018..fd1df5ae 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/AppUtils.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/AppUtils.java @@ -4,6 +4,7 @@ import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; +import com.stardust.autojs.runtime.JavascriptInterface; import com.stardust.util.IntentUtil; import java.util.List; @@ -21,6 +22,7 @@ public class AppUtils { } + @JavascriptInterface public boolean launchPackage(String packageName) { try { PackageManager packageManager = mContext.getPackageManager(); @@ -32,10 +34,12 @@ public class AppUtils { } + @JavascriptInterface public boolean launchApp(String appName) { return launchPackage(getPackageName(appName)); } + @JavascriptInterface public String getPackageName(String appName) { PackageManager packageManager = mContext.getPackageManager(); List installedApplications = packageManager.getInstalledApplications(PackageManager.GET_META_DATA); @@ -47,6 +51,7 @@ public class AppUtils { return ""; } + @JavascriptInterface public boolean openAppSetting(String packageName) { return IntentUtil.goToAppDetailSettings(mContext, packageName); } diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/Console.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/Console.java index 1e4da8a7..aa07c961 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/Console.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/Console.java @@ -2,30 +2,42 @@ package com.stardust.autojs.runtime.api; import android.support.annotation.Nullable; +import com.stardust.autojs.runtime.JavascriptInterface; + /** * Created by Stardust on 2017/4/2. */ public interface Console { + @JavascriptInterface void i(@Nullable Object o); + @JavascriptInterface void i(String str); + @JavascriptInterface void e(String message); + @JavascriptInterface void e(@Nullable Object o); + @JavascriptInterface void v(String v); + @JavascriptInterface void v(@Nullable Object o); + @JavascriptInterface void log(@Nullable Object o); + @JavascriptInterface void log(String string); + @JavascriptInterface void show(); + @JavascriptInterface void clear(); } diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/api/UiSelector.java b/autojs/src/main/java/com/stardust/autojs/runtime/api/UiSelector.java index 561f4113..edc2c04d 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/api/UiSelector.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/api/UiSelector.java @@ -6,12 +6,14 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import com.stardust.autojs.runtime.AccessibilityBridge; +import com.stardust.autojs.runtime.JavascriptInterface; import com.stardust.automator.AccessibilityEventCommandHost; import com.stardust.automator.ActionArgument; import com.stardust.automator.UiGlobalSelector; import com.stardust.automator.UiObject; import com.stardust.automator.UiObjectCollection; import com.stardust.automator.filter.DfsFilter; +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS; import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_COLUMN_INT; @@ -51,7 +53,7 @@ import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat. public class UiSelector extends UiGlobalSelector { - private class FindCommand implements AccessibilityEventCommandHost.Command { + private class FindCommand extends AccessibilityEventCommandHost.AbstractCommand { UiObjectCollection result; @@ -62,6 +64,7 @@ public class UiSelector extends UiGlobalSelector { result = findOf(root); } } + } private static final String TAG = "UiSelector"; @@ -73,11 +76,17 @@ public class UiSelector extends UiGlobalSelector { } + @JavascriptInterface public UiObjectCollection find() { ensureAccessibilityServiceEnabled(); - FindCommand command = new FindCommand(); - mAccessibilityBridge.getCommandHost().executeAndWaitForEvent(command); - return command.result; + AccessibilityService service = mAccessibilityBridge.getService(); + if (service != null) { + AccessibilityNodeInfo root = service.getRootInActiveWindow(); + if (root != null) { + return findOf(root); + } + } + return null; } private void ensureAccessibilityServiceEnabled() { @@ -85,6 +94,7 @@ public class UiSelector extends UiGlobalSelector { } + @JavascriptInterface @NonNull public UiObjectCollection untilFind() { UiObjectCollection uiObjectCollection; @@ -94,6 +104,7 @@ public class UiSelector extends UiGlobalSelector { return uiObjectCollection; } + @JavascriptInterface public UiObject findOne() { return untilFindOne(); } @@ -104,6 +115,7 @@ public class UiSelector extends UiGlobalSelector { return new UiObject(collection.get(0).getInfo()); } + @JavascriptInterface public UiSelector id(final String id) { if (!id.contains(":")) { addFilter(new DfsFilter() { @@ -120,110 +132,135 @@ public class UiSelector extends UiGlobalSelector { } - public boolean performAction(int action, ActionArgument... arguments) { + private boolean performAction(int action, ActionArgument... arguments) { return untilFind().performAction(action, arguments); } + @JavascriptInterface public boolean click() { return performAction(ACTION_CLICK); } + @JavascriptInterface public boolean longClick() { return performAction(ACTION_LONG_CLICK); } + @JavascriptInterface public boolean accessibilityFocus() { return performAction(ACTION_ACCESSIBILITY_FOCUS); } + @JavascriptInterface public boolean clearAccessibilityFocus() { return performAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS); } + @JavascriptInterface public boolean focus() { return performAction(ACTION_FOCUS); } + @JavascriptInterface public boolean clearFocus() { return performAction(ACTION_CLEAR_FOCUS); } + @JavascriptInterface public boolean copy() { return performAction(ACTION_COPY); } + @JavascriptInterface public boolean paste() { return performAction(ACTION_PASTE); } + @JavascriptInterface public boolean select() { return performAction(ACTION_SELECT); } + @JavascriptInterface public boolean cut() { return performAction(ACTION_CUT); } + @JavascriptInterface public boolean collapse() { return performAction(ACTION_COLLAPSE); } + @JavascriptInterface public boolean expand() { return performAction(ACTION_EXPAND); } + @JavascriptInterface public boolean dismiss() { return performAction(ACTION_DISMISS); } + @JavascriptInterface public boolean show() { return performAction(ACTION_SHOW_ON_SCREEN.getId()); } + @JavascriptInterface public boolean scrollForward() { return performAction(ACTION_SCROLL_FORWARD); } + @JavascriptInterface public boolean scrollBackward() { return performAction(ACTION_SCROLL_BACKWARD); } + @JavascriptInterface public boolean scrollUp() { return performAction(ACTION_SCROLL_UP.getId()); } + @JavascriptInterface public boolean scrollDown() { return performAction(ACTION_SCROLL_DOWN.getId()); } + @JavascriptInterface public boolean scrollLeft() { return performAction(ACTION_SCROLL_LEFT.getId()); } + @JavascriptInterface public boolean scrollRight() { return performAction(ACTION_SCROLL_RIGHT.getId()); } + @JavascriptInterface public boolean contextClick() { return performAction(ACTION_CONTEXT_CLICK.getId()); } + @JavascriptInterface public boolean setSelection(int s, int e) { return performAction(ACTION_SET_SELECTION, new ActionArgument.IntActionArgument(ACTION_ARGUMENT_SELECTION_START_INT, s), new ActionArgument.IntActionArgument(ACTION_ARGUMENT_SELECTION_END_INT, e)); } + @JavascriptInterface public boolean setText(String text) { return performAction(ACTION_SET_TEXT, new ActionArgument.CharSequenceActionArgument(ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text)); } + @JavascriptInterface public boolean setProgress(float value) { return performAction(ACTION_SET_PROGRESS.getId(), new ActionArgument.FloatActionArgument(ACTION_ARGUMENT_PROGRESS_VALUE, value)); } + @JavascriptInterface public boolean scrollTo(int row, int column) { return performAction(ACTION_SCROLL_TO_POSITION.getId(), new ActionArgument.IntActionArgument(ACTION_ARGUMENT_ROW_INT, row), diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/ActionAutomator.java b/autojs/src/main/java/com/stardust/autojs/runtime/simple_action/SimpleActionAutomator.java similarity index 82% rename from autojs/src/main/java/com/stardust/autojs/runtime/action/ActionAutomator.java rename to autojs/src/main/java/com/stardust/autojs/runtime/simple_action/SimpleActionAutomator.java index 61b65566..bf2ce6cb 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/ActionAutomator.java +++ b/autojs/src/main/java/com/stardust/autojs/runtime/simple_action/SimpleActionAutomator.java @@ -1,9 +1,10 @@ -package com.stardust.autojs.runtime.action; +package com.stardust.autojs.runtime.simple_action; import android.accessibilityservice.AccessibilityService; import android.graphics.Rect; import android.os.Build; import android.support.annotation.RequiresApi; +import android.util.Log; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -11,14 +12,17 @@ import com.stardust.autojs.runtime.AccessibilityBridge; import com.stardust.autojs.runtime.JavascriptInterface; import com.stardust.autojs.runtime.ScriptRuntime; import com.stardust.automator.AccessibilityEventCommandHost; +import com.stardust.automator.simple_action.SimpleAction; +import com.stardust.automator.simple_action.ActionFactory; +import com.stardust.automator.simple_action.ActionTarget; /** * Created by Stardust on 2017/4/2. */ -public class ActionAutomator { +public class SimpleActionAutomator { - private static class PerformGlobalActionCommand implements AccessibilityEventCommandHost.Command { + private static class PerformGlobalActionCommand extends AccessibilityEventCommandHost.AbstractCommand { boolean result; private int mGlobalAction; @@ -31,12 +35,13 @@ public class ActionAutomator { public void execute(AccessibilityService service, AccessibilityEvent event) { result = service.performGlobalAction(mGlobalAction); } + } private AccessibilityBridge mAccessibilityBridge; private ScriptRuntime mScriptRuntime; - public ActionAutomator(AccessibilityBridge accessibilityBridge, ScriptRuntime scriptRuntime) { + public SimpleActionAutomator(AccessibilityBridge accessibilityBridge, ScriptRuntime scriptRuntime) { mAccessibilityBridge = accessibilityBridge; mScriptRuntime = scriptRuntime; } @@ -159,9 +164,10 @@ public class ActionAutomator { private boolean performGlobalAction(final int action) { ensureAccessibilityServiceEnabled(); - PerformGlobalActionCommand command = new PerformGlobalActionCommand(action); - mAccessibilityBridge.getCommandHost().executeAndWaitForEvent(command); - return command.result; + AccessibilityService service = mAccessibilityBridge.getService(); + if (service == null) + return false; + return service.performGlobalAction(action); } @JavascriptInterface @@ -174,17 +180,16 @@ public class ActionAutomator { } @SuppressWarnings("unchecked") - private T performAction(Action action) { + private T performAction(SimpleAction simpleAction) { ensureAccessibilityServiceEnabled(); - mAccessibilityBridge.getActionPerformHost().addAction(action); - synchronized (action) { - try { - action.wait(); - } catch (InterruptedException e) { - action.setValid(false); - mScriptRuntime.stoppedByInterrupted(e); + AccessibilityService service = mAccessibilityBridge.getService(); + if (service != null) { + AccessibilityNodeInfo root = service.getRootInActiveWindow(); + Log.i("Automator", "performAction: " + simpleAction + " root = " + root); + if (root != null) { + simpleAction.setResult(simpleAction.perform(root)); } } - return (T) action.getResult(); + return (T) simpleAction.getResult(); } } 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 ef8e5464..4033913d 100644 --- a/autojs/src/main/java/com/stardust/autojs/script/ScriptSource.java +++ b/autojs/src/main/java/com/stardust/autojs/script/ScriptSource.java @@ -66,4 +66,8 @@ public abstract class ScriptSource implements Serializable { return mode; } + @Override + public String toString() { + return mName + ".js"; + } } diff --git a/autojs/src/main/java/com/stardust/autojs/script/StringScriptSource.java b/autojs/src/main/java/com/stardust/autojs/script/StringScriptSource.java index 6d10c722..68de27fd 100644 --- a/autojs/src/main/java/com/stardust/autojs/script/StringScriptSource.java +++ b/autojs/src/main/java/com/stardust/autojs/script/StringScriptSource.java @@ -23,4 +23,5 @@ public class StringScriptSource extends ScriptSource { public String getScript() { return mScript; } + } diff --git a/automator/src/main/java/com/stardust/automator/AccessibilityEventCommandHost.java b/automator/src/main/java/com/stardust/automator/AccessibilityEventCommandHost.java index e18ec2f4..e1967fa7 100644 --- a/automator/src/main/java/com/stardust/automator/AccessibilityEventCommandHost.java +++ b/automator/src/main/java/com/stardust/automator/AccessibilityEventCommandHost.java @@ -3,6 +3,7 @@ package com.stardust.automator; import android.accessibilityservice.AccessibilityService; import android.util.Log; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; import com.stardust.view.accessibility.AccessibilityDelegate; @@ -21,6 +22,24 @@ public class AccessibilityEventCommandHost implements AccessibilityDelegate { public interface Command { void execute(AccessibilityService service, AccessibilityEvent event); + + boolean isValid(); + + void setValid(boolean valid); + } + + public abstract static class AbstractCommand implements Command { + + private boolean mValid; + + public synchronized boolean isValid() { + return mValid; + } + + public synchronized void setValid(boolean valid) { + mValid = valid; + } + } private static final String TAG = "CommandHostDelegate"; @@ -31,8 +50,8 @@ public class AccessibilityEventCommandHost implements AccessibilityDelegate { private final Queue mCommands = new LinkedList<>(); - private Executor mExecutor = Executors.newFixedThreadPool(5); - private int mRunMode = 0; + private Executor mExecutor = Executors.newSingleThreadExecutor(); + private int mRunMode = RUN_MODE_THREAD_POOL; @Override public boolean onAccessibilityEvent(final AccessibilityService service, final AccessibilityEvent event) { @@ -42,20 +61,24 @@ public class AccessibilityEventCommandHost implements AccessibilityDelegate { } while (!mCommands.isEmpty()) { final Command command = mCommands.poll(); - executeCommand(command, service, event); + if (command.isValid()) { + executeCommand(command, service, event); + } } } return false; } private void executeCommand(final Command command, final AccessibilityService service, final AccessibilityEvent event) { - Runnable r = new Runnable() { + final Runnable r = new Runnable() { @Override public void run() { + if (!command.isValid()) { + return; + } Log.v(TAG, "executing " + command); command.execute(service, event); synchronized (command) { - Log.v(TAG, "notify " + mCommands.size() + " commands"); command.notify(); } } @@ -78,6 +101,7 @@ public class AccessibilityEventCommandHost implements AccessibilityDelegate { try { command.wait(); } catch (InterruptedException e) { + command.setValid(false); throw new RuntimeException(e); } } diff --git a/automator/src/main/java/com/stardust/automator/GC.java b/automator/src/main/java/com/stardust/automator/GC.java deleted file mode 100644 index 94f622ff..00000000 --- a/automator/src/main/java/com/stardust/automator/GC.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.stardust.automator; - -import android.view.accessibility.AccessibilityNodeInfo; - -/** - * Created by Stardust on 2017/3/9. - */ - -public class GC { - - -} diff --git a/automator/src/main/java/com/stardust/automator/UiBridge.java b/automator/src/main/java/com/stardust/automator/UiBridge.java deleted file mode 100644 index 8ddb01c6..00000000 --- a/automator/src/main/java/com/stardust/automator/UiBridge.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.stardust.automator; - -/** - * Created by Stardust on 2017/3/9. - */ - -public class UiBridge { - - - public UiBridge() { - } - - - -} diff --git a/automator/src/main/java/com/stardust/automator/UiGlobalSelector.java b/automator/src/main/java/com/stardust/automator/UiGlobalSelector.java index ce802f7f..9a3ad4b6 100644 --- a/automator/src/main/java/com/stardust/automator/UiGlobalSelector.java +++ b/automator/src/main/java/com/stardust/automator/UiGlobalSelector.java @@ -13,6 +13,7 @@ import com.stardust.automator.filter.TextFilter; import com.stardust.automator.filter.ClassNameFilter; import com.stardust.automator.filter.DescFilter; import com.stardust.automator.filter.ListFilter; +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; import java.util.ArrayList; import java.util.LinkedList; @@ -360,6 +361,15 @@ public class UiGlobalSelector { return UiObjectCollection.of(list); } + public UiObjectCollection findOf(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo node) { + List list = new ArrayList<>(); + list.add(node); + for (ListFilter filter : mFilters) { + list = filter.filter(list); + } + return UiObjectCollection.of(list); + } + public UiObject findOneOf(AccessibilityNodeInfo node) { // TODO: 2017/3/9 优化 return new UiObject(findOf(node).get(0).getInfo()); diff --git a/automator/src/main/java/com/stardust/automator/UiObject.java b/automator/src/main/java/com/stardust/automator/UiObject.java index fd3cb2c8..4c917148 100644 --- a/automator/src/main/java/com/stardust/automator/UiObject.java +++ b/automator/src/main/java/com/stardust/automator/UiObject.java @@ -1,10 +1,15 @@ package com.stardust.automator; +import android.graphics.Rect; import android.os.Bundle; import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.view.accessibility.AccessibilityNodeInfo; +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; +import com.stardust.view.accessibility.AccessibilityNodeInfoHelper; + import java.util.ArrayList; +import java.util.List; import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CONTEXT_CLICK; import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN; @@ -21,10 +26,17 @@ import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat. public class UiObject extends AccessibilityNodeInfoCompat { + private AccessibilityNodeInfoAllocator mAllocator = null; + public UiObject(Object info) { super(info); } + public UiObject(Object info, AccessibilityNodeInfoAllocator allocator) { + super(info); + mAllocator = allocator; + } + public UiObject parent() { return new UiObject(getParent().getInfo()); } @@ -49,6 +61,22 @@ public class UiObject extends AccessibilityNodeInfoCompat { return UiObjectCollection.ofCompat(list); } + public int childCount() { + return getChildCount(); + } + + public Rect bounds() { + return AccessibilityNodeInfoHelper.getBoundsInScreen(this); + } + + public Rect boundsInParent() { + return AccessibilityNodeInfoHelper.getBoundsInParent(this); + } + + public int drawingOrder() { + return getDrawingOrder(); + } + public String id() { return getViewIdResourceName(); } @@ -188,4 +216,33 @@ public class UiObject extends AccessibilityNodeInfoCompat { new ActionArgument.IntActionArgument(ACTION_ARGUMENT_COLUMN_INT, column)); } + @Override + public AccessibilityNodeInfoCompat getChild(int index) { + if (mAllocator == null) + return super.getChild(index); + return mAllocator.getChild(this, index); + } + + @Override + public AccessibilityNodeInfoCompat getParent() { + if (mAllocator == null) + return super.getParent(); + return mAllocator.getParent(this); + } + + @Override + public List findAccessibilityNodeInfosByText(String text) { + if (mAllocator == null) + return super.findAccessibilityNodeInfosByText(text); + return mAllocator.findAccessibilityNodeInfosByText(this, text); + } + + @Override + public List findAccessibilityNodeInfosByViewId(String viewId) { + if (mAllocator == null) + return super.findAccessibilityNodeInfosByViewId(viewId); + return mAllocator.findAccessibilityNodeInfosByViewId(this, viewId); + } + + } diff --git a/automator/src/main/java/com/stardust/automator/filter/DfsFilter.java b/automator/src/main/java/com/stardust/automator/filter/DfsFilter.java index 2061f121..149df3e1 100644 --- a/automator/src/main/java/com/stardust/automator/filter/DfsFilter.java +++ b/automator/src/main/java/com/stardust/automator/filter/DfsFilter.java @@ -2,6 +2,8 @@ package com.stardust.automator.filter; import android.view.accessibility.AccessibilityNodeInfo; +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; + import java.util.ArrayList; import java.util.List; @@ -34,7 +36,7 @@ public abstract class DfsFilter implements ListFilter, Filter { private void filterChildren(AccessibilityNodeInfo parent, List list) { for (int i = 0; i < parent.getChildCount(); i++) { - AccessibilityNodeInfo child = parent.getChild(i); + AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(parent, i); if (child == null) continue; boolean included = isIncluded(child); diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/Able.java b/automator/src/main/java/com/stardust/automator/simple_action/Able.java similarity index 97% rename from autojs/src/main/java/com/stardust/autojs/runtime/action/Able.java rename to automator/src/main/java/com/stardust/automator/simple_action/Able.java index 99806ba7..1ef092bc 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/Able.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/Able.java @@ -1,4 +1,4 @@ -package com.stardust.autojs.runtime.action; +package com.stardust.automator.simple_action; import android.util.SparseArray; import android.view.accessibility.AccessibilityNodeInfo; diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/ActionFactory.java b/automator/src/main/java/com/stardust/automator/simple_action/ActionFactory.java similarity index 79% rename from autojs/src/main/java/com/stardust/autojs/runtime/action/ActionFactory.java rename to automator/src/main/java/com/stardust/automator/simple_action/ActionFactory.java index fd87b4a0..e93b7e30 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/ActionFactory.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/ActionFactory.java @@ -1,4 +1,4 @@ -package com.stardust.autojs.runtime.action; +package com.stardust.automator.simple_action; import android.graphics.Rect; import android.os.Build; @@ -26,14 +26,14 @@ public class ActionFactory { .entry(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null) .map(); - public static Action createActionWithTextFilter(int action, String text, int index) { + public static SimpleAction createActionWithTextFilter(int action, String text, int index) { if (searchUpAction.containsKey(action)) return new SearchUpTargetAction(action, new FilterAction.TextFilter(text, index)); else return new DepthFirstSearchTargetAction(action, new FilterAction.TextFilter(text, index)); } - public static Action createActionWithBoundsFilter(int action, Rect rect) { + public static SimpleAction createActionWithBoundsFilter(int action, Rect rect) { if (searchUpAction.containsKey(action)) return new SearchUpTargetAction(action, new FilterAction.BoundsFilter(rect)); else @@ -41,7 +41,7 @@ public class ActionFactory { } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) - public static Action createActionWithEditableFilter(int action, int index, final String text) { + public static SimpleAction createActionWithEditableFilter(int action, int index, final String text) { return new SearchTargetAction(action, new FilterAction.EditableFilter(index)) { @Override @@ -53,15 +53,15 @@ public class ActionFactory { }; } - public static Action createScrollMaxAction(int action) { + public static SimpleAction createScrollMaxAction(int action) { return new ScrollMaxAction(action); } - public static Action createScrollAction(int action, int i) { + public static SimpleAction createScrollAction(int action, int i) { return new ScrollAction(action, i); } - public static Action createActionWithIdFilter(int action, String id) { + public static SimpleAction createActionWithIdFilter(int action, String id) { return new FilterAction.SimpleFilterAction(action, new FilterAction.IdFilter(id)); } } diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/ActionTarget.java b/automator/src/main/java/com/stardust/automator/simple_action/ActionTarget.java similarity index 78% rename from autojs/src/main/java/com/stardust/autojs/runtime/action/ActionTarget.java rename to automator/src/main/java/com/stardust/automator/simple_action/ActionTarget.java index cfb4bea4..fd3388d4 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/ActionTarget.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/ActionTarget.java @@ -1,4 +1,4 @@ -package com.stardust.autojs.runtime.action; +package com.stardust.automator.simple_action; import android.graphics.Rect; import android.os.Build; @@ -10,7 +10,7 @@ import android.support.annotation.RequiresApi; public interface ActionTarget { - Action createAction(int action, Object... params); + SimpleAction createAction(int action, Object... params); class TextActionTarget implements ActionTarget { @@ -23,7 +23,7 @@ public interface ActionTarget { } @Override - public Action createAction(int action, Object... params) { + public SimpleAction createAction(int action, Object... params) { return ActionFactory.createActionWithTextFilter(action, mText, mIndex); } } @@ -37,7 +37,7 @@ public interface ActionTarget { } @Override - public Action createAction(int action, Object... params) { + public SimpleAction createAction(int action, Object... params) { return ActionFactory.createActionWithBoundsFilter(action, mBoundsInRect); } } @@ -51,7 +51,7 @@ public interface ActionTarget { } @Override - public Action createAction(int action, Object... params) { + public SimpleAction createAction(int action, Object... params) { return ActionFactory.createActionWithEditableFilter(action, mIndex, params[0].toString()); } } @@ -64,7 +64,7 @@ public interface ActionTarget { } @Override - public Action createAction(int action, Object... params) { + public SimpleAction createAction(int action, Object... params) { return ActionFactory.createActionWithIdFilter(action, mId); } } diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/DepthFirstSearchTargetAction.java b/automator/src/main/java/com/stardust/automator/simple_action/DepthFirstSearchTargetAction.java similarity index 76% rename from autojs/src/main/java/com/stardust/autojs/runtime/action/DepthFirstSearchTargetAction.java rename to automator/src/main/java/com/stardust/automator/simple_action/DepthFirstSearchTargetAction.java index 8542e5dd..daf7684e 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/DepthFirstSearchTargetAction.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/DepthFirstSearchTargetAction.java @@ -1,7 +1,9 @@ -package com.stardust.autojs.runtime.action; +package com.stardust.automator.simple_action; import android.view.accessibility.AccessibilityNodeInfo; +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; + /** * Created by Stardust on 2017/1/27. */ @@ -10,6 +12,7 @@ public class DepthFirstSearchTargetAction extends SearchTargetAction { private Able mAble; + private static final long TIME_LIMIT = 500; public DepthFirstSearchTargetAction(int action, Filter filter) { super(action, filter); @@ -24,7 +27,7 @@ public class DepthFirstSearchTargetAction extends SearchTargetAction { if (mAble.isAble(n)) return n; for (int i = 0; i < n.getChildCount(); i++) { - AccessibilityNodeInfo child = n.getChild(i); + AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(n, i); if (child == null) continue; AccessibilityNodeInfo node = searchTarget(child); diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/FilterAction.java b/automator/src/main/java/com/stardust/automator/simple_action/FilterAction.java similarity index 68% rename from autojs/src/main/java/com/stardust/autojs/runtime/action/FilterAction.java rename to automator/src/main/java/com/stardust/automator/simple_action/FilterAction.java index 25d5050c..dd5a0e56 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/FilterAction.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/FilterAction.java @@ -1,8 +1,10 @@ -package com.stardust.autojs.runtime.action; +package com.stardust.automator.simple_action; import android.graphics.Rect; import android.view.accessibility.AccessibilityNodeInfo; +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; + import java.util.Collections; import java.util.LinkedList; import java.util.List; @@ -11,7 +13,7 @@ import java.util.List; * Created by Stardust on 2017/1/27. */ -public abstract class FilterAction extends Action { +public abstract class FilterAction extends SimpleAction { public interface Filter { @@ -31,14 +33,21 @@ public abstract class FilterAction extends Action { @Override public List filter(AccessibilityNodeInfo root) { - - List list = root.findAccessibilityNodeInfosByText(mText); + List list = AccessibilityNodeInfoAllocator.getGlobal().findAccessibilityNodeInfosByText(root, mText); if (mIndex == -1) return list; if (mIndex >= list.size()) return Collections.EMPTY_LIST; return Collections.singletonList(list.get(mIndex)); } + + @Override + public String toString() { + return "TextFilter{" + + "mText='" + mText + '\'' + + ", mIndex=" + mIndex + + '}'; + } } public static class BoundsFilter implements Filter { @@ -63,7 +72,7 @@ public abstract class FilterAction extends Action { return root; } for (int i = 0; i < root.getChildCount(); i++) { - AccessibilityNodeInfo child = root.getChild(i); + AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(root, i); if (child == null) continue; AccessibilityNodeInfo nodeInfo = findAccessibilityNodeInfosByBounds(child); @@ -74,6 +83,13 @@ public abstract class FilterAction extends Action { } return null; } + + @Override + public String toString() { + return "BoundsFilter{" + + "mBoundsInScreen=" + mBoundsInScreen + + '}'; + } } public static class EditableFilter implements Filter { @@ -94,20 +110,26 @@ public abstract class FilterAction extends Action { return Collections.singletonList(editableList.get(mIndex)); } - @SuppressWarnings("unchecked") public static List findEditable(AccessibilityNodeInfo root) { if (root == null) { - return Collections.EMPTY_LIST; + return Collections.emptyList(); } if (root.isEditable()) { return Collections.singletonList(root); } List list = new LinkedList<>(); for (int i = 0; i < root.getChildCount(); i++) { - list.addAll(findEditable(root.getChild(i))); + list.addAll(findEditable(AccessibilityNodeInfoAllocator.getGlobal().getChild(root, i))); } return list; } + + @Override + public String toString() { + return "EditableFilter{" + + "mIndex=" + mIndex + + '}'; + } } public static class IdFilter implements Filter { @@ -120,7 +142,14 @@ public abstract class FilterAction extends Action { @Override public List filter(AccessibilityNodeInfo root) { - return root.findAccessibilityNodeInfosByViewId(mId); + return AccessibilityNodeInfoAllocator.getGlobal().findAccessibilityNodeInfosByViewId(root, mId); + } + + @Override + public String toString() { + return "IdFilter{" + + "mId='" + mId + '\'' + + '}'; } } @@ -134,7 +163,10 @@ public abstract class FilterAction extends Action { public boolean perform(AccessibilityNodeInfo root) { if (root == null) return false; - return perform(mFilter.filter(root)); + List list = mFilter.filter(root); + boolean succeed = perform(list); + AccessibilityNodeInfoAllocator.recycleList(root, list); + return succeed; } public abstract boolean perform(List nodes); @@ -150,11 +182,11 @@ public abstract class FilterAction extends Action { @Override public boolean perform(List nodes) { - if(nodes == null || nodes.isEmpty()) + if (nodes == null || nodes.isEmpty()) return false; boolean succeed = true; - for(AccessibilityNodeInfo nodeInfo : nodes){ - if(!nodeInfo.performAction(mAction)){ + for (AccessibilityNodeInfo nodeInfo : nodes) { + if (!nodeInfo.performAction(mAction)) { succeed = false; } } @@ -162,4 +194,10 @@ public abstract class FilterAction extends Action { } } + @Override + public String toString() { + return "FilterAction{" + + "mFilter=" + mFilter + + '}'; + } } diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/GetTextAction.java b/automator/src/main/java/com/stardust/automator/simple_action/GetTextAction.java similarity index 77% rename from autojs/src/main/java/com/stardust/autojs/runtime/action/GetTextAction.java rename to automator/src/main/java/com/stardust/automator/simple_action/GetTextAction.java index f9bf9205..8089aca6 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/GetTextAction.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/GetTextAction.java @@ -1,7 +1,9 @@ -package com.stardust.autojs.runtime.action; +package com.stardust.automator.simple_action; import android.view.accessibility.AccessibilityNodeInfo; +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; + import java.util.ArrayList; import java.util.List; @@ -9,7 +11,7 @@ import java.util.List; * Created by Stardust on 2017/2/14. */ -public class GetTextAction extends Action { +public class GetTextAction extends SimpleAction { @Override @@ -26,7 +28,7 @@ public class GetTextAction extends Action { texts.add(text.toString()); } for (int i = 0; i < nodeInfo.getChildCount(); i++) { - AccessibilityNodeInfo child = nodeInfo.getChild(i); + AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(nodeInfo, i); if (child != null) { getText(child, texts); child.recycle(); diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/ScrollAction.java b/automator/src/main/java/com/stardust/automator/simple_action/ScrollAction.java similarity index 85% rename from autojs/src/main/java/com/stardust/autojs/runtime/action/ScrollAction.java rename to automator/src/main/java/com/stardust/automator/simple_action/ScrollAction.java index 57d14038..a3df08e2 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/ScrollAction.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/ScrollAction.java @@ -1,14 +1,16 @@ -package com.stardust.autojs.runtime.action; +package com.stardust.automator.simple_action; import android.view.accessibility.AccessibilityNodeInfo; +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; + import java.util.ArrayList; import java.util.List; /** * Created by Stardust on 2017/2/12. */ -public class ScrollAction extends Action { +public class ScrollAction extends SimpleAction { private int mIndex, mAction; @@ -46,7 +48,7 @@ public class ScrollAction extends Action { list.add(node); } for (int i = 0; i < node.getChildCount(); i++) { - AccessibilityNodeInfo child = node.getChild(i); + AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(node, i); if (child == null) continue; if (!findScrollableNodes(child, list)) diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/ScrollMaxAction.java b/automator/src/main/java/com/stardust/automator/simple_action/ScrollMaxAction.java similarity index 88% rename from autojs/src/main/java/com/stardust/autojs/runtime/action/ScrollMaxAction.java rename to automator/src/main/java/com/stardust/automator/simple_action/ScrollMaxAction.java index b306ab36..d153c14d 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/ScrollMaxAction.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/ScrollMaxAction.java @@ -1,14 +1,16 @@ -package com.stardust.autojs.runtime.action; +package com.stardust.automator.simple_action; import android.graphics.Rect; import android.util.Log; import android.view.accessibility.AccessibilityNodeInfo; +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; + /** * Created by Stardust on 2017/1/27. */ -public class ScrollMaxAction extends Action { +public class ScrollMaxAction extends SimpleAction { private static final String TAG = ScrollMaxAction.class.getSimpleName(); private int mScrollAction; @@ -49,7 +51,7 @@ public class ScrollMaxAction extends Action { } } for (int i = 0; i < nodeInfo.getChildCount(); i++) { - AccessibilityNodeInfo child = nodeInfo.getChild(i); + AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(nodeInfo, i); if (child != null) { findMaxScrollableNodeInfo(child); if (mMaxScrollableNode != child) { diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/SearchTargetAction.java b/automator/src/main/java/com/stardust/automator/simple_action/SearchTargetAction.java similarity index 75% rename from autojs/src/main/java/com/stardust/autojs/runtime/action/SearchTargetAction.java rename to automator/src/main/java/com/stardust/automator/simple_action/SearchTargetAction.java index 6730e244..16bab407 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/SearchTargetAction.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/SearchTargetAction.java @@ -1,4 +1,4 @@ -package com.stardust.autojs.runtime.action; +package com.stardust.automator.simple_action; import android.view.accessibility.AccessibilityNodeInfo; @@ -26,6 +26,7 @@ public abstract class SearchTargetAction extends FilterAction { if (node != null) { performAction(node); performed = true; + node.recycle(); } } return performed; @@ -35,7 +36,7 @@ public abstract class SearchTargetAction extends FilterAction { node.performAction(mAction); } - public int getAction(){ + public int getAction() { return mAction; } @@ -43,5 +44,11 @@ public abstract class SearchTargetAction extends FilterAction { return node; } - + @Override + public String toString() { + return "SearchTargetAction{" + + "mAction=" + mAction + ", " + + super.toString() + + "}"; + } } diff --git a/automator/src/main/java/com/stardust/automator/simple_action/SearchUpTargetAction.java b/automator/src/main/java/com/stardust/automator/simple_action/SearchUpTargetAction.java new file mode 100644 index 00000000..38645204 --- /dev/null +++ b/automator/src/main/java/com/stardust/automator/simple_action/SearchUpTargetAction.java @@ -0,0 +1,50 @@ +package com.stardust.automator.simple_action; + +import android.view.accessibility.AccessibilityNodeInfo; + +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by Stardust on 2017/1/27. + */ + +public class SearchUpTargetAction extends SearchTargetAction { + + private static final String TAG = SearchUpTargetAction.class.getSimpleName(); + private static final int LOOP_MAX = 20; + private Able mAble; + + public SearchUpTargetAction(int action, Filter filter) { + super(action, filter); + mAble = Able.ABLE_MAP.get(action); + } + + @Override + public AccessibilityNodeInfo searchTarget(AccessibilityNodeInfo n) { + AccessibilityNodeInfo node = n; + int i = 0; + List list = new ArrayList<>(); + while (node != null && !mAble.isAble(node)) { + i++; + if (i > LOOP_MAX) { + node.recycle(); + return null; + } + AccessibilityNodeInfo parent = AccessibilityNodeInfoAllocator.getGlobal().getParent(node); + list.add(node); + node = parent; + } + return node; + } + + @Override + public String toString() { + return "SearchUpTargetAction{" + + "mAble=" + mAble + ", " + + super.toString() + + '}'; + } +} diff --git a/autojs/src/main/java/com/stardust/autojs/runtime/action/Action.java b/automator/src/main/java/com/stardust/automator/simple_action/SimpleAction.java similarity index 56% rename from autojs/src/main/java/com/stardust/autojs/runtime/action/Action.java rename to automator/src/main/java/com/stardust/automator/simple_action/SimpleAction.java index f33a141b..4dfa05a0 100644 --- a/autojs/src/main/java/com/stardust/autojs/runtime/action/Action.java +++ b/automator/src/main/java/com/stardust/automator/simple_action/SimpleAction.java @@ -1,4 +1,4 @@ -package com.stardust.autojs.runtime.action; +package com.stardust.automator.simple_action; import android.view.accessibility.AccessibilityNodeInfo; @@ -6,26 +6,26 @@ import android.view.accessibility.AccessibilityNodeInfo; * Created by Stardust on 2017/1/27. */ -public abstract class Action { +public abstract class SimpleAction { private boolean mValid = true; private Object mResult = false; public abstract boolean perform(AccessibilityNodeInfo root); - public Object getResult() { + public synchronized Object getResult() { return mResult; } - public void setResult(Object result) { + public synchronized void setResult(Object result) { mResult = result; } - public void setValid(boolean valid) { + public synchronized void setValid(boolean valid) { mValid = valid; } - public boolean isValid() { + public synchronized boolean isValid() { return mValid; } } diff --git a/automator/src/main/java/com/stardust/automator/simple_action/SimpleActionPerformHost.java b/automator/src/main/java/com/stardust/automator/simple_action/SimpleActionPerformHost.java new file mode 100644 index 00000000..cbf24530 --- /dev/null +++ b/automator/src/main/java/com/stardust/automator/simple_action/SimpleActionPerformHost.java @@ -0,0 +1,90 @@ +package com.stardust.automator.simple_action; + +import android.accessibilityservice.AccessibilityService; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.util.Log; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator; +import com.stardust.view.accessibility.AccessibilityDelegate; + +import java.util.LinkedList; +import java.util.Queue; +import java.util.Vector; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + + +/** + * Created by Stardust on 2017/1/21. + */ + +public class SimpleActionPerformHost implements AccessibilityDelegate { + + private static final String TAG = "ActionPerformDelegate"; + + private BlockingQueue mSimpleAction = new ArrayBlockingQueue<>(1); + private Executor mExecutor = Executors.newSingleThreadExecutor(); + + public void addToActionQueueAndWait(SimpleAction simpleAction) throws InterruptedException { + mSimpleAction.put(simpleAction); + synchronized (simpleAction) { + simpleAction.wait(); + } + } + + @Override + public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) { + SimpleAction action = mSimpleAction.poll(); + if (action == null) + return false; + AccessibilityNodeInfo root = service.getRootInActiveWindow(); + if (root == null) { + return false; + } + performAction(root, action); + return false; + } + + private void performAction(AccessibilityNodeInfo root, SimpleAction simpleAction) { + mExecutor.execute(new SimpleActionPerformRunnable(root, simpleAction)); + } + + + private synchronized void onActionPerformed(SimpleAction simpleAction) { + AccessibilityNodeInfoAllocator.getGlobal().recycleAll(); + synchronized (simpleAction) { + simpleAction.notify(); + } + } + + + private class SimpleActionPerformRunnable implements Runnable { + + private final SimpleAction mSimpleAction; + private final AccessibilityNodeInfo mRoot; + + SimpleActionPerformRunnable(AccessibilityNodeInfo root, SimpleAction simpleAction) { + mRoot = root; + mSimpleAction = simpleAction; + } + + @Override + public void run() { + if (!mSimpleAction.isValid()) { + return; + } + Log.i(TAG, "perform simpleAction: " + mSimpleAction); + if (mSimpleAction.perform(mRoot)) { + mSimpleAction.setResult(true); + onActionPerformed(mSimpleAction); + } + } + } + +} + diff --git a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityAllocator.java b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityAllocator.java deleted file mode 100644 index 0140d090..00000000 --- a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityAllocator.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.stardust.view.accessibility; - -import android.view.accessibility.AccessibilityNodeInfo; - -import java.util.List; -import java.util.Vector; - -/** - * Created by Stardust on 2017/3/22. - */ - -public class AccessibilityAllocator { - - - private List mAccessibilityNodeInfoList = new Vector<>(); - - public AccessibilityNodeInfo getChild(AccessibilityNodeInfo parent, int i) { - return add(parent.getChild(i)); - } - - private AccessibilityNodeInfo add(AccessibilityNodeInfo nodeInfo) { - mAccessibilityNodeInfoList.add(nodeInfo); - return nodeInfo; - } - - public int recycleAll() { - int recycled = 0; - for (AccessibilityNodeInfo nodeInfo : mAccessibilityNodeInfoList) { - try { - nodeInfo.recycle(); - recycled++; - } catch (IllegalStateException ignored) { - } - } - return recycled; - } -} diff --git a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityDelegate.java b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityDelegate.java index d3bb7f23..95a47df5 100644 --- a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityDelegate.java +++ b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityDelegate.java @@ -2,6 +2,7 @@ package com.stardust.view.accessibility; import android.accessibilityservice.AccessibilityService; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; /** * Created by Stardust on 2017/2/14. diff --git a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityInfoProvider.java b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityInfoProvider.java index a4a7b88a..c3eef6db 100644 --- a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityInfoProvider.java +++ b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityInfoProvider.java @@ -6,6 +6,7 @@ import android.content.Context; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; import com.stardust.view.accessibility.AccessibilityDelegate; diff --git a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityNodeInfoAllocator.java b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityNodeInfoAllocator.java new file mode 100644 index 00000000..6b229cc6 --- /dev/null +++ b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityNodeInfoAllocator.java @@ -0,0 +1,196 @@ +package com.stardust.view.accessibility; + +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; +import android.util.Log; +import android.view.accessibility.AccessibilityNodeInfo; + +import com.stardust.automator.BuildConfig; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Created by Stardust on 2017/3/22. + */ + +public class AccessibilityNodeInfoAllocator { + + public static final AccessibilityNodeInfoAllocator NONE = new NoOpAllocator(); + + private static final String TAG = "AccessibilityAllocator"; + private static final boolean DEBUG = BuildConfig.DEBUG; + + + private static final AccessibilityNodeInfoAllocator GLOBAL = new AccessibilityNodeInfoAllocator(); + + public static AccessibilityNodeInfoAllocator getGlobal() { + return GLOBAL; + } + + private Map mAccessibilityNodeInfoList = new HashMap<>(); + + public AccessibilityNodeInfo getChild(AccessibilityNodeInfo parent, int i) { + return add(parent.getChild(i)); + } + + public AccessibilityNodeInfoCompat getChild(AccessibilityNodeInfoCompat parent, int i) { + AccessibilityNodeInfoCompat compat = parent.getChild(i); + add((AccessibilityNodeInfo) compat.getInfo()); + return compat; + } + + public AccessibilityNodeInfo getParent(AccessibilityNodeInfo n) { + return add(n.getParent()); + } + + + public AccessibilityNodeInfoCompat getParent(AccessibilityNodeInfoCompat n) { + AccessibilityNodeInfoCompat compat = n.getParent(); + add((AccessibilityNodeInfo) compat.getInfo()); + return compat; + } + + public List findAccessibilityNodeInfosByText(AccessibilityNodeInfo root, String text) { + List list = root.findAccessibilityNodeInfosByText(text); + addAll(list); + return list; + } + + + public List findAccessibilityNodeInfosByText(AccessibilityNodeInfoCompat root, String text) { + List list = root.findAccessibilityNodeInfosByText(text); + addAll(list); + return list; + } + + public List findAccessibilityNodeInfosByViewId(AccessibilityNodeInfo root, String id) { + List list = root.findAccessibilityNodeInfosByViewId(id); + addAll(list); + return list; + } + + + public List findAccessibilityNodeInfosByViewId(AccessibilityNodeInfoCompat root, String id) { + List list = root.findAccessibilityNodeInfosByViewId(id); + addAll(list); + return list; + } + + public void recycle(AccessibilityNodeInfo nodeInfo) { + nodeInfo.recycle(); + mAccessibilityNodeInfoList.remove(nodeInfo); + } + + public void recycle(AccessibilityNodeInfoCompat nodeInfo) { + recycle((AccessibilityNodeInfo) nodeInfo.getInfo()); + } + + public int recycleAll() { + int notRecycledCount = 0; + int size = mAccessibilityNodeInfoList.size(); + for (Map.Entry pair : mAccessibilityNodeInfoList.entrySet()) { + try { + pair.getKey().recycle(); + notRecycledCount++; + if (DEBUG) + Log.w(TAG, pair.getValue()); + } catch (IllegalStateException ignored) { + } + } + Log.i(TAG, "Total: " + size + " Not recycled: " + notRecycledCount); + return notRecycledCount; + } + + private AccessibilityNodeInfo add(AccessibilityNodeInfo nodeInfo) { + String stackTrace = DEBUG ? Arrays.toString(Thread.currentThread().getStackTrace()) : null; + mAccessibilityNodeInfoList.put(nodeInfo, stackTrace); + return nodeInfo; + } + + private void addAll(Collection nodeInfos) { + String stackTrace = DEBUG ? Arrays.toString(Thread.currentThread().getStackTrace()) : null; + for (Object nodeInfo : nodeInfos) { + if (nodeInfo instanceof AccessibilityNodeInfo) { + mAccessibilityNodeInfoList.put((AccessibilityNodeInfo) nodeInfo, stackTrace); + } else if (nodeInfo instanceof AccessibilityNodeInfoCompat) { + mAccessibilityNodeInfoList.put((AccessibilityNodeInfo) ((AccessibilityNodeInfoCompat) nodeInfo).getInfo(), stackTrace); + } else { + throw new IllegalArgumentException("nodeInfo: " + nodeInfo + " nodeInfos:" + nodeInfos); + } + } + } + + public static void recycleList(AccessibilityNodeInfo root, List list) { + for (AccessibilityNodeInfo nodeInfo : list) { + if (nodeInfo != root) { + nodeInfo.recycle(); + } + } + } + + private static class NoOpAllocator extends AccessibilityNodeInfoAllocator { + + @Override + public AccessibilityNodeInfoCompat getParent(AccessibilityNodeInfoCompat n) { + return n.getParent(); + } + + @Override + public AccessibilityNodeInfo getParent(AccessibilityNodeInfo n) { + return n.getParent(); + } + + @Override + public AccessibilityNodeInfoCompat getChild(AccessibilityNodeInfoCompat parent, int i) { + return parent.getChild(i); + } + + @Override + public AccessibilityNodeInfo getChild(AccessibilityNodeInfo parent, int i) { + return parent.getChild(i); + } + + @Override + public List findAccessibilityNodeInfosByViewId(AccessibilityNodeInfoCompat root, String id) { + return root.findAccessibilityNodeInfosByViewId(id); + } + + + @Override + public List findAccessibilityNodeInfosByViewId(AccessibilityNodeInfo root, String id) { + return root.findAccessibilityNodeInfosByViewId(id); + } + + @Override + public List findAccessibilityNodeInfosByText(AccessibilityNodeInfo root, String text) { + return root.findAccessibilityNodeInfosByText(text); + } + + @Override + public List findAccessibilityNodeInfosByText(AccessibilityNodeInfoCompat root, String text) { + return root.findAccessibilityNodeInfosByText(text); + } + + @Override + public void recycle(AccessibilityNodeInfo nodeInfo) { + super.recycle(nodeInfo); + } + + @Override + public void recycle(AccessibilityNodeInfoCompat nodeInfo) { + super.recycle(nodeInfo); + } + + @Override + public int recycleAll() { + return -1; + } + + + } + + +} diff --git a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityNodeInfoHelper.java b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityNodeInfoHelper.java index 970da5e3..e84d2e71 100644 --- a/automator/src/main/java/com/stardust/view/accessibility/AccessibilityNodeInfoHelper.java +++ b/automator/src/main/java/com/stardust/view/accessibility/AccessibilityNodeInfoHelper.java @@ -1,8 +1,11 @@ package com.stardust.view.accessibility; import android.graphics.Rect; +import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat; import android.view.accessibility.AccessibilityNodeInfo; +import com.stardust.automator.UiObject; + /** * Created by Stardust on 2017/3/6. */ @@ -45,4 +48,16 @@ public class AccessibilityNodeInfoHelper { nodeInfo.getBoundsInScreen(rect); return rect; } + + public static Rect getBoundsInScreen(AccessibilityNodeInfoCompat nodeInfo) { + Rect rect = new Rect(); + nodeInfo.getBoundsInScreen(rect); + return rect; + } + + public static Rect getBoundsInParent(AccessibilityNodeInfoCompat nodeInfo) { + Rect rect = new Rect(); + nodeInfo.getBoundsInParent(rect); + return rect; + } } diff --git a/common/src/main/java/com/stardust/pio/PFile.java b/common/src/main/java/com/stardust/pio/PFile.java index 28d3ccad..3eea9bd1 100644 --- a/common/src/main/java/com/stardust/pio/PFile.java +++ b/common/src/main/java/com/stardust/pio/PFile.java @@ -1,6 +1,7 @@ package com.stardust.pio; import android.content.Context; +import android.content.res.AssetManager; import android.util.Log; import java.io.File; @@ -239,4 +240,12 @@ public class PFile { } return file.delete(); } + + public static String readAsset(AssetManager assets, String path) { + try { + return read(assets.open(path)); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } } diff --git a/common/src/main/java/com/stardust/util/AssetsCache.java b/common/src/main/java/com/stardust/util/AssetsCache.java index cbabed6d..5458a7d7 100644 --- a/common/src/main/java/com/stardust/util/AssetsCache.java +++ b/common/src/main/java/com/stardust/util/AssetsCache.java @@ -1,8 +1,12 @@ package com.stardust.util; +import android.app.Activity; import android.content.res.AssetManager; +import android.support.v4.app.FragmentActivity; +import com.stardust.pio.PFile; + import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -13,15 +17,20 @@ import java.util.Map; public class AssetsCache { - private Map mCache = new HashMap<>(); + private static final long PERSIST_TIME = 5 * 60 * 1000; + private static SimpleCache cache = new SimpleCache<>(PERSIST_TIME, 5, 30 * 1000); - public String read(AssetManager manager, String path) throws IOException { - String str = mCache.get(path); - if (str == null) { - str = FileUtils.readString(manager.open(path)); - mCache.put(path, str); - } - return str; + public static String get(final AssetManager assetManager, final String path) { + return cache.get(path, new SimpleCache.Supplier() { + @Override + public String get(String key) { + return PFile.readAsset(assetManager, path); + } + }); + } + + public static String get(final Activity activity, final String path) { + return get(activity.getAssets(), path); } } diff --git a/common/src/main/java/com/stardust/util/Consumer.java b/common/src/main/java/com/stardust/util/Consumer.java index 4ab22f40..590d7244 100644 --- a/common/src/main/java/com/stardust/util/Consumer.java +++ b/common/src/main/java/com/stardust/util/Consumer.java @@ -1,9 +1,11 @@ package com.stardust.util; +import android.support.annotation.Keep; + /** * Created by Stardust on 2017/3/10. */ - +@Keep public interface Consumer { void accept(T t); diff --git a/common/src/main/java/com/stardust/util/FileSorter.java b/common/src/main/java/com/stardust/util/FileSorter.java index ae32368e..020e0440 100644 --- a/common/src/main/java/com/stardust/util/FileSorter.java +++ b/common/src/main/java/com/stardust/util/FileSorter.java @@ -1,8 +1,10 @@ package com.stardust.util; import java.io.File; +import java.text.Collator; import java.util.Arrays; import java.util.Comparator; +import java.util.Locale; /** * Created by Stardust on 2017/3/31. @@ -11,12 +13,13 @@ import java.util.Comparator; public class FileSorter { public static void sort(File[] files) { + final Collator collator = Collator.getInstance(); Arrays.sort(files, new Comparator() { @Override public int compare(File o1, File o2) { if (o1.isDirectory() != o2.isDirectory()) return o1.isDirectory() ? Integer.MIN_VALUE : Integer.MAX_VALUE; - return o1.getName().compareTo(o2.getName()); + return collator.compare(o1.getName(), o2.getName()); } }); } diff --git a/common/src/main/java/com/stardust/util/FileUtils.java b/common/src/main/java/com/stardust/util/FileUtils.java deleted file mode 100644 index 84765c18..00000000 --- a/common/src/main/java/com/stardust/util/FileUtils.java +++ /dev/null @@ -1,263 +0,0 @@ -package com.stardust.util; - -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; - - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.lang.reflect.Field; -import java.net.URISyntaxException; - -/** - * Created by Stardust on 2017/1/23. - */ - -public class FileUtils { - - private static final int BUFFER_SIZE = 1024 * 100; - - public static String getPath(Context context, Uri uri) throws URISyntaxException { - if ("content".equalsIgnoreCase(uri.getScheme())) { - String[] projection = {"_data"}; - Cursor cursor = null; - - try { - cursor = context.getContentResolver().query(uri, projection, null, null, null); - int column_index = cursor.getColumnIndexOrThrow("_data"); - if (cursor.moveToFirst()) { - return cursor.getString(column_index); - } - } catch (Exception ignored) { - } finally { - if (cursor != null) - cursor.close(); - } - } else if ("file".equalsIgnoreCase(uri.getScheme())) { - return uri.getPath(); - } - - return null; - } - - public static String getPath(InputStream inputStream) { - if (inputStream instanceof FileInputStream) { - FileInputStream fis = (FileInputStream) inputStream; - try { - Field field = fis.getClass().getDeclaredField("path"); - field.setAccessible(true); - return (String) field.get(fis); - } catch (NoSuchFieldException | IllegalAccessException e) { - e.printStackTrace(); - } - } - return null; - } - - public static boolean createFileIfNotExists(String path) { - ensureFolder(path); - File file = new File(path); - if (!file.exists()) { - try { - return file.createNewFile(); - } catch (IOException e) { - e.printStackTrace(); - } - } - return false; - } - - private static boolean ensureFolder(String path) { - int i = path.lastIndexOf("\\"); - if (i < 0) - i = path.lastIndexOf("/"); - if (i >= 0) { - String folder = path.substring(0, i); - File file = new File(folder); - if (file.exists()) - return true; - return file.mkdirs(); - } else { - return false; - } - } - - public static String readString(File file, String encoding) { - try { - return readString(new FileInputStream(file), encoding); - } catch (FileNotFoundException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - public static String readString(File file) { - return readString(file, "utf-8"); - } - - public static String readString(InputStream is, String encoding) { - try { - byte[] bytes = new byte[is.available()]; - is.read(bytes); - return new String(bytes, encoding); - } catch (IOException e) { - e.printStackTrace(); - throw new RuntimeException(e); - } - } - - public static String readString(InputStream inputStream) { - return readString(inputStream, "utf-8"); - } - - public static boolean copy(Context context, int rawId, String path) { - InputStream is = context.getResources().openRawResource(rawId); - return copy(is, path); - } - - public static boolean copy(InputStream is, String path) { - if (!ensureFolder(path)) - return false; - File file = new File(path); - try { - if (!file.exists()) - if (!file.createNewFile()) - return false; - FileOutputStream fos = new FileOutputStream(file); - return copy(is, fos); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - - private static boolean copy(InputStream is, OutputStream os) { - byte[] buffer = new byte[BUFFER_SIZE]; - try { - while (is.available() > 0) { - int n = is.read(buffer); - os.write(buffer, 0, n); - } - is.close(); - os.close(); - return true; - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - - - public static boolean copy(String pathFrom, String pathTo) { - try { - return copy(new FileInputStream(pathFrom), pathTo); - } catch (FileNotFoundException e) { - e.printStackTrace(); - return false; - } - } - - public static boolean copyAsset(Context context, String assetFile, String path) { - try { - return copy(context.getAssets().open(assetFile), path); - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - - public static String renameWithoutExtension(String path, String newName) { - File file = new File(path); - File newFile = new File(file.getParent(), newName + "." + getExtension(file.getName())); - file.renameTo(newFile); - return newFile.getAbsolutePath(); - } - - public static String getExtension(String fileName) { - int i = fileName.lastIndexOf('.'); - if (i < 0) - return ""; - return fileName.substring(i + 1); - } - - - public static boolean writeString(String path, String text) { - return writeString(new File(path), text); - } - - public static boolean writeString(File file, String text) { - try { - return writeString(new FileOutputStream(file), text); - } catch (FileNotFoundException e) { - e.printStackTrace(); - return false; - } - } - - public static boolean writeString(OutputStream outputStream, String text) { - try { - outputStream.write(text.getBytes()); - return true; - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - - public static String generateNotExistingPath(String path, String extension) { - if (!new File(path + extension).exists()) - return path + extension; - int i = 0; - while (true) { - String pathI = path + "(" + i + ")" + extension; - if (!new File(pathI).exists()) - return pathI; - i++; - } - - } - - public static String getNameWithoutExtension(String fileName) { - int a = fileName.lastIndexOf('/'); - if (a < 0) - a = fileName.lastIndexOf('\\'); - int b = fileName.lastIndexOf('.'); - if (a < 0) - a = -1; - if (b < 0) - b = fileName.length(); - fileName = fileName.substring(a + 1, b); - return fileName; - } - - public static File copyAssetToTmpFile(Context context, String path) { - String extension = getExtension(path); - String name = getNameWithoutExtension(path); - if (name.length() < 5) { - name += name.hashCode(); - } - try { - File tmpFile = File.createTempFile(name, "." + extension, context.getCacheDir()); - copyAsset(context, path, tmpFile.getPath()); - return tmpFile; - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - public static boolean deleteAll(File file) { - if (file.isFile()) - return file.delete(); - for (File child : file.listFiles()) { - if (!deleteAll(child)) - return false; - } - return file.delete(); - } -} \ No newline at end of file diff --git a/common/src/main/java/com/stardust/util/SimpleCache.java b/common/src/main/java/com/stardust/util/SimpleCache.java new file mode 100644 index 00000000..22e76e27 --- /dev/null +++ b/common/src/main/java/com/stardust/util/SimpleCache.java @@ -0,0 +1,118 @@ +package com.stardust.util; + +import java.util.Iterator; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + +/** + * Created by Stardust on 2017/4/5. + */ + +public class SimpleCache { + + public interface Supplier { + T get(String key); + } + + private long mPersistTime; + private LimitedHashMap> mCache; + private Timer mCacheCheckTimer; + private Supplier mSupplier; + + public SimpleCache(long persistTime, int cacheSize, long checkInterval, Supplier supplier) { + mPersistTime = persistTime; + mCache = new LimitedHashMap<>(cacheSize); + mSupplier = supplier == null ? new NullSupplier() : supplier; + mCacheCheckTimer = new Timer(); + startCacheCheck(checkInterval); + } + + public SimpleCache(long persistTime, int cacheSize, long checkInterval) { + this(persistTime, cacheSize, checkInterval, null); + } + + public synchronized void put(String key, T value) { + mCache.put(key, new Item<>(value)); + } + + public synchronized T get(String key) { + Item item = mCache.get(key); + if (item == null) { + T value = mSupplier.get(key); + if (value != null) { + put(key, value); + } + return value; + } + return item.value; + } + + public T get(String key, T defaultValue) { + T value = get(key); + if (value == null) { + value = defaultValue; + } + return value; + } + + public T get(String key, Supplier supplier) { + T value = get(key); + if (value == null) { + value = supplier.get(key); + put(key, value); + } + return value; + } + + public synchronized void destroy() { + mCacheCheckTimer.cancel(); + mCache.clear(); + } + + private void startCacheCheck(long checkInterval) { + mCacheCheckTimer.schedule(new TimerTask() { + @Override + public void run() { + checkCache(); + } + }, 0, checkInterval); + } + + + private synchronized void checkCache() { + Iterator>> iterator = mCache.entrySet().iterator(); + while (iterator.hasNext()) { + if (!iterator.next().getValue().isValid()) { + iterator.remove(); + } + } + } + + private class Item { + + T value; + private long mSaveMillis; + + + Item(T value) { + mSaveMillis = System.currentTimeMillis(); + this.value = value; + } + + boolean isValid() { + return System.currentTimeMillis() - mSaveMillis <= mPersistTime; + } + + } + + private static class NullSupplier implements Supplier { + + @Override + public T get(String key) { + return null; + } + } + + +}