Attempt to fix selector memeory leak. Add update checker

This commit is contained in:
hyb1996
2017-04-09 14:02:57 +08:00
parent 6844eb9841
commit 0e980c2e6a
72 changed files with 1282 additions and 415 deletions

View File

@@ -8,8 +8,8 @@ android {
applicationId "com.stardust.scriptdroid"
minSdkVersion 19
targetSdkVersion 23
versionCode 114
versionName "2.0.7 Alpha"
versionCode 116
versionName "2.0.7 Alpha3"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
@@ -17,7 +17,7 @@ android {
buildTypes {
release {
shrinkResources true
minifyEnabled true
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
@@ -75,6 +75,7 @@ dependencies {
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['
compile 'com.android.volley:volley:1.0.0'
compile(name: 'libtermexec-release', ext: 'aar')
compile(name: 'emulatorview-release', ext: 'aar')
compile(name: 'term-debug', ext: 'aar')

View File

@@ -24,3 +24,61 @@
-dontwarn jackpal.androidterm.**
-dontwarn com.iwebpp.nodeandroid.**
-dontwarn org.msgpack.core.**
-keep class org.mozilla.javascript.** { *; }
-keep class com.jecelyin.editor.** { *; }
-keep class com.stardust.automator.** { *; }
-keep class com.stardust.autojs.** { *; }
-keep class org.greenrobot.eventbus.** { *; }
-keep class * extends c
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keepclassmembers class ** {
@com.stardust.autojs.runtime.JavascriptInterface <methods>;
}
-keep class org.msgpack.** { *; }
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclassmembers public class * extends android.view.View {
void set*(***);
*** get*();
}
-keep class * extends android.support.v4.app.Fragment {
<methods>;
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
-keepclassmembers class **.R$* {
public static <fields>;
}
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
-keepattributes Signature
-keepattributes EnclosingMethod

View File

@@ -13,6 +13,7 @@ import com.stardust.app.VolumeChangeObserver;
import com.stardust.scriptdroid.autojs.AutoJs;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.tool.CrashHandler;
import com.stardust.scriptdroid.tool.UpdateChecker;
import com.stardust.scriptdroid.ui.error.ErrorReportActivity;
import com.stardust.theme.ThemeColor;
import com.stardust.theme.ThemeColorManager;

View File

@@ -16,7 +16,7 @@ public class Pref {
private static SharedPreferences.OnSharedPreferenceChangeListener onSharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (key.equals(App.getResString(R.string.key_run_mode))) {
if (key.equals(getString(R.string.key_run_mode))) {
AutoJs.getInstance().getCommandHost().setRunMode(getRunModeFromValue(sharedPreferences.getString(key, null)));
}
}
@@ -33,14 +33,10 @@ public class Pref {
}
}
public static SharedPreferences def() {
private static SharedPreferences def() {
return PreferenceManager.getDefaultSharedPreferences(App.getApp());
}
public static boolean isFirstEnableAssistMode() {
return getDisposableBoolean("isFirstEnableAssistMode", true);
}
private static boolean getDisposableBoolean(String key, boolean defaultValue) {
boolean b = DISPOSABLE_BOOLEAN.getBoolean(key, defaultValue);
if (b == defaultValue) {
@@ -65,9 +61,20 @@ public class Pref {
return def().getBoolean(getString(R.string.key_use_volume_control_running), false);
}
public static String getStartRecordTrigger() {
return def().getString(getString(R.string.key_start_record_trigger), null);
}
public static String getStopRecordTrigger() {
return def().getString(getString(R.string.key_stop_record_trigger), null);
}
public static boolean enableAccessibilityServiceByRoot() {
return def().getBoolean(getString(R.string.key_enable_accessibility_service_by_root), false);
}
private static String getString(int id) {
return App.getApp().getString(id);
return App.getResString(id);
}
public static int MaxTextLengthForCodeCompletion() {
@@ -85,4 +92,9 @@ public class Pref {
static {
def().registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);
}
public static boolean isEditActivityFirstUsing() {
return getDisposableBoolean("Still Love Eating 17.4.6", true);
}
}

View File

@@ -63,16 +63,6 @@ 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() {
@@ -114,8 +104,8 @@ public class AutoJs implements AccessibilityBridge {
if (AccessibilityServiceUtils.isAccessibilityServiceEnabled(App.getApp(), AccessibilityWatchDogService.class)) {
errorMessage = App.getApp().getString(R.string.text_auto_operate_service_enabled_but_not_running);
} else {
if (Pref.def().getBoolean(App.getApp().getString(R.string.key_enable_accessibility_service_by_root), false)) {
if (!AccessibilityServiceTool.enableAccessibilityServiceByRootAndWaitFor(AccessibilityWatchDogService.class, 3000)) {
if (Pref.enableAccessibilityServiceByRoot()) {
if (!AccessibilityServiceTool.enableAccessibilityServiceByRootAndWaitFor(2000)) {
errorMessage = App.getApp().getString(R.string.text_enable_accessibility_service_by_root_timeout);
}
} else {

View File

@@ -18,7 +18,7 @@ import android.view.View;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.floating_window.content.MainMenuNavigatorContent;
import com.stardust.scriptdroid.external.floating_window.content.RecordNavigatorContent;
import com.stardust.scriptdroid.external.floating_window.content.ScritpListNavigatorContent;
import com.stardust.scriptdroid.external.floating_window.content.ScriptListNavigatorContent;
import com.stardust.theme.ThemeColorManagerCompat;
import java.util.ArrayList;
@@ -52,7 +52,7 @@ public class HoverMenuAdapter implements io.mattcarroll.hover.HoverMenuAdapter {
mData.put(HoverMenuAdapter.ID_MAIN, new MainMenuNavigatorContent(context));
mData.put(HoverMenuAdapter.ID_RECORD, new RecordNavigatorContent(context));
mData.put(HoverMenuAdapter.ID_SCRIPT_LIST, new ScritpListNavigatorContent(context));
mData.put(HoverMenuAdapter.ID_SCRIPT_LIST, new ScriptListNavigatorContent(context));
mTabIds = new ArrayList<>();
for (String tabId : mData.keySet()) {

View File

@@ -0,0 +1,104 @@
package com.stardust.scriptdroid.external.floating_window.content;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.stardust.autojs.script.FileScriptSource;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.autojs.AutoJs;
import com.stardust.scriptdroid.external.floating_window.HoverMenuService;
import com.stardust.scriptdroid.scripts.ScriptFile;
import com.stardust.scriptdroid.scripts.StorageScriptProvider;
import com.stardust.scriptdroid.ui.edit.EditActivity;
import com.stardust.scriptdroid.ui.main.script_list.ScriptAndFolderListRecyclerView;
import com.stardust.scriptdroid.ui.main.script_list.ScriptListRecyclerView;
import com.stardust.scriptdroid.ui.main.script_list.ScriptListWithProgressBarView;
import com.stardust.util.MessageEvent;
import com.stardust.widget.ViewHolderSupplier;
import io.mattcarroll.hover.Navigator;
import io.mattcarroll.hover.NavigatorContent;
/**
* Created by Stardust on 2017/3/12.
*/
public class ScriptListNavigatorContent implements NavigatorContent {
private ViewHolderSupplier<ScriptAndFolderListRecyclerView.ViewHolder> mViewHolderSupplier = new ViewHolderSupplier<ScriptAndFolderListRecyclerView.ViewHolder>() {
@Override
public ScriptAndFolderListRecyclerView.ViewHolder createViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case ScriptAndFolderListRecyclerView.VIEW_TYPE_FILE:
return new FileViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.floating_script_list_recycler_view_file, parent, false));
case ScriptAndFolderListRecyclerView.VIEW_TYPE_DIRECTORY:
return new DirectoryViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.floating_script_list_recycler_view_directory, parent, false));
}
return null;
}
};
private ScriptAndFolderListRecyclerView mFloatingScriptFileListView;
private ScriptListWithProgressBarView mScriptListWithProgressBarView;
public ScriptListNavigatorContent(Context context) {
mScriptListWithProgressBarView = new ScriptListWithProgressBarView(new ContextThemeWrapper(context, R.style.AppTheme));
mFloatingScriptFileListView = mScriptListWithProgressBarView.getScriptAndFolderListRecyclerView();
mFloatingScriptFileListView.setViewHolderSupplier(mViewHolderSupplier);
mFloatingScriptFileListView.setStorageScriptProvider(StorageScriptProvider.getDefault());
mFloatingScriptFileListView.setOnItemClickListener(new ScriptAndFolderListRecyclerView.OnScriptFileClickListener() {
@Override
public void onClick(ScriptFile file, int position) {
AutoJs.getInstance().getScriptEngineService().execute(new FileScriptSource(file));
HoverMenuService.postEvent(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU));
}
});
}
@NonNull
@Override
public View getView() {
return mScriptListWithProgressBarView;
}
@Override
public void onShown(@NonNull Navigator navigator) {
}
@Override
public void onHidden() {
}
private class DirectoryViewHolder extends ScriptAndFolderListRecyclerView.ViewHolder {
DirectoryViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(mFloatingScriptFileListView.getOnItemClickListenerProxy());
itemView.setOnLongClickListener(mFloatingScriptFileListView.getOnItemLongClickListenerProxy());
}
}
private class FileViewHolder extends DirectoryViewHolder {
FileViewHolder(View itemView) {
super(itemView);
itemView.findViewById(R.id.edit).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = mFloatingScriptFileListView.getChildViewHolder((View) v.getParent()).getAdapterPosition();
ScriptFile file = mFloatingScriptFileListView.getAdapter().getScriptFileAt(position);
EditActivity.editFile(v.getContext(), file);
}
});
}
}
}

View File

@@ -1,43 +0,0 @@
package com.stardust.scriptdroid.external.floating_window.content;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.ContextThemeWrapper;
import android.view.View;
import com.stardust.scriptdroid.scripts.ScriptFileList;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.floating_window.view.FloatingScriptFileListView;
import io.mattcarroll.hover.Navigator;
import io.mattcarroll.hover.NavigatorContent;
/**
* Created by Stardust on 2017/3/12.
*/
public class ScritpListNavigatorContent implements NavigatorContent {
private FloatingScriptFileListView mFloatingScriptFileListView;
public ScritpListNavigatorContent(Context context) {
mFloatingScriptFileListView = new FloatingScriptFileListView(new ContextThemeWrapper(context, R.style.AppTheme));
mFloatingScriptFileListView.setScriptFileList(ScriptFileList.getImpl());
}
@NonNull
@Override
public View getView() {
return mFloatingScriptFileListView;
}
@Override
public void onShown(@NonNull Navigator navigator) {
}
@Override
public void onHidden() {
}
}

View File

@@ -3,7 +3,6 @@ package com.stardust.scriptdroid.external.floating_window.view;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
@@ -173,7 +172,7 @@ public class FloatingScriptFileListView extends RecyclerView {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(getContext()).inflate(R.layout.floating_script_list_recycler_view_item, parent, false);
View itemView = LayoutInflater.from(getContext()).inflate(R.layout.floating_script_list_recycler_view_file, parent, false);
return new ViewHolder(itemView);
}
@@ -199,7 +198,7 @@ public class FloatingScriptFileListView extends RecyclerView {
name = (TextView) itemView.findViewById(R.id.name);
path = (TextView) itemView.findViewById(R.id.path);
ViewUtil.$(itemView, R.id.edit).setOnClickListener(mOnEditIconClickListener);
ViewUtil.$(itemView, R.id.more).setOnClickListener(mOnMoreIconClickListener);
//ViewUtil.$(itemView, R.id.more).setOnClickListener(mOnMoreIconClickListener);
itemView.setOnClickListener(mOnItemClickListener);
}
}

View File

@@ -46,7 +46,7 @@ public class TaskPrefEditActivity extends AbstractAppCompatPluginActivity {
scriptList.setCurrentDirectory(StorageScriptProvider.DEFAULT_DIRECTORY);
scriptList.setOnItemClickListener(new ScriptAndFolderListRecyclerView.OnScriptFileClickListener() {
@Override
public void onClick(ScriptFile file) {
public void onClick(ScriptFile file, int position) {
mSelectedScriptFilePath = file.getPath();
finish();
}

View File

@@ -56,7 +56,7 @@ public class NodeInfo {
}
private String simplifyId(String idResourceName) {
if(idResourceName == null)
if (idResourceName == null)
return null;
int i = idResourceName.indexOf('/');
return idResourceName.substring(i + 1);
@@ -75,13 +75,15 @@ public class NodeInfo {
}
public static NodeInfo capture(@NonNull AccessibilityNodeInfo root) {
AccessibilityNodeInfoAllocator allocator = new AccessibilityNodeInfoAllocator();
NodeInfo nodeInfo = new NodeInfo(root);
for (int i = 0; i < root.getChildCount(); i++) {
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(root, i);
AccessibilityNodeInfo child = allocator.getChild(root, i);
if (child != null) {
nodeInfo.children.add(capture(child));
}
}
allocator.recycleAll();
return nodeInfo;
}

View File

@@ -9,6 +9,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.automator.simple_action.FilterAction;
import com.stardust.scriptdroid.layout_inspector.NodeInfo;
import com.stardust.util.SparseArrayEntries;
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
import com.stardust.view.accessibility.AccessibilityNodeInfoHelper;
import org.greenrobot.eventbus.EventBus;
@@ -121,11 +122,13 @@ public class AccessibilityActionConverter {
AccessibilityNodeInfo source = event.getSource();
if (source == null)
return;
List<AccessibilityNodeInfo> editableList = FilterAction.EditableFilter.findEditable(service.getRootInActiveWindow());
AccessibilityNodeInfoAllocator allocator = new AccessibilityNodeInfoAllocator();
List<AccessibilityNodeInfo> editableList = FilterAction.EditableFilter.findEditable(allocator, service.getRootInActiveWindow());
int i = findInEditableList(editableList, source);
recycle(editableList);
sb.append("while(!input(").append(i).append(", \"").append(source.getText()).append("\"));");
source.recycle();
allocator.recycleAll();
}
private void recycle(List<AccessibilityNodeInfo> list) {

View File

@@ -18,8 +18,8 @@ public class TouchRecorder extends InputEventToJsRecorder {
}
private void setUpTriggers() {
setStartTriggerKey(Pref.def().getString(App.getResString(R.string.key_start_record_trigger), null));
setStopTriggerKey(Pref.def().getString(App.getResString(R.string.key_stop_record_trigger), null));
setStartTriggerKey(Pref.getStartRecordTrigger());
setStopTriggerKey(Pref.getStopRecordTrigger());
}

View File

@@ -2,6 +2,7 @@ package com.stardust.scriptdroid.scripts;
import android.os.Environment;
import com.android.dex.util.FileUtils;
import com.stardust.pio.PFile;
import java.io.File;
@@ -24,7 +25,7 @@ public class ScriptFile extends File {
}
private void init() {
mSimplifiedName = PFile.getNameWithoutExtension(getPath());
mSimplifiedName = PFile.getNameWithoutExtension(getName());
mSimplifyPath = getPath();
if (mSimplifyPath.startsWith(Environment.getExternalStorageDirectory().getPath())) {
mSimplifyPath = mSimplifyPath.substring(Environment.getExternalStorageDirectory().getPath().length());
@@ -37,7 +38,14 @@ public class ScriptFile extends File {
}
public boolean renameTo(String newName) {
return renameTo(new File(getParent(), newName));
if (isDirectory())
return renameTo(new File(getParent(), newName));
else
return renameTo(new File(getParent(), newName + getExtension()));
}
private String getExtension() {
return PFile.getExtension(getName());
}
public String getSimplifiedPath() {
@@ -91,4 +99,7 @@ public class ScriptFile extends File {
return mSimplifiedName;
}
public boolean moveTo(ScriptFile to) {
return renameTo(new File(to, getName()));
}
}

View File

@@ -4,12 +4,16 @@ import android.accessibilityservice.AccessibilityService;
import android.os.Build;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.autojs.runtime.JavascriptInterface;
import com.stardust.view.accessibility.AccessibilityDelegate;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import org.greenrobot.eventbus.EventBus;
import java.lang.ref.WeakReference;
import java.util.Map;
import java.util.SortedMap;
@@ -26,7 +30,8 @@ public class AccessibilityWatchDogService extends AccessibilityService {
private static final String TAG = "AccessibilityWatchDog";
private static final SortedMap<Integer, AccessibilityDelegate> mDelegates = new TreeMap<>();
private static WeakReference<AccessibilityWatchDogService> instance;
private static final Object LOCK = new Object();
private static AccessibilityWatchDogService instance;
private Executor mExecutor = Executors.newSingleThreadExecutor();
public static void addDelegate(int uniquePriority, AccessibilityDelegate delegate) {
@@ -38,9 +43,7 @@ public class AccessibilityWatchDogService extends AccessibilityService {
}
public static AccessibilityWatchDogService getInstance() {
if (instance == null)
return null;
return instance.get();
return instance;
}
@Override
@@ -58,24 +61,52 @@ public class AccessibilityWatchDogService extends AccessibilityService {
});
}
@Override
public AccessibilityNodeInfo getRootInActiveWindow() {
try {
return super.getRootInActiveWindow();
} catch (IllegalStateException e) {
e.printStackTrace();
return null;
}
}
@Override
public void onInterrupt() {
}
@Override
public void onDestroy() {
instance = null;
super.onDestroy();
}
@Override
protected void onServiceConnected() {
super.onServiceConnected();
// FIXME: 2017/2/12 有时在无障碍中开启服务后这里不会调用服务也不会运行,安卓的BUG???
Log.v(TAG, "onServiceConnected");
instance = new WeakReference<>(this);
instance = this;
super.onServiceConnected();
synchronized (LOCK) {
LOCK.notifyAll();
}
// FIXME: 2017/2/12 有时在无障碍中开启服务后这里不会调用服务也不会运行,安卓的BUG???
}
public static void disable() {
if (instance != null && instance.get() != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
instance.get().disableSelf();
if (instance != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
instance.disableSelf();
} else {
AccessibilityServiceTool.goToAccessibilitySetting();
}
}
public static void waitForEnabled(long timeOut) {
synchronized (LOCK) {
try {
LOCK.wait(timeOut);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -1,7 +1,10 @@
package com.stardust.scriptdroid.tool;
import android.accessibilityservice.AccessibilityService;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.stardust.scriptdroid.Pref;
@@ -11,6 +14,8 @@ import com.stardust.scriptdroid.R;
import com.stardust.util.Shell;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import java.util.Locale;
import static com.stardust.view.accessibility.AccessibilityServiceUtils.isAccessibilityServiceEnabled;
/**
@@ -20,8 +25,8 @@ import static com.stardust.view.accessibility.AccessibilityServiceUtils.isAccess
public class AccessibilityServiceTool {
public static void enableAccessibilityService() {
if (Pref.def().getBoolean(App.getApp().getString(R.string.key_enable_accessibility_service_by_root), false)) {
if (!enableAccessibilityServiceByRootAndWaitFor(AccessibilityWatchDogService.class, 3000)) {
if (Pref.enableAccessibilityServiceByRoot()) {
if (!enableAccessibilityServiceByRoot(AccessibilityWatchDogService.class)) {
goToAccessibilitySetting();
}
} else {
@@ -41,28 +46,32 @@ public class AccessibilityServiceTool {
}
}
public static boolean enableAccessibilityServiceByRootAndWaitFor(Class<AccessibilityWatchDogService> accessibilityService, int timeout) {
Shell.execCommand("settings put secure enabled_accessibility_services %accessibility:"
+ App.getApp().getPackageName() + "/" + accessibilityService.getName(), true);
long millis = System.currentTimeMillis();
while (true) {
if (isAccessibilityServiceEnabled(App.getApp(), accessibilityService))
return true;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (System.currentTimeMillis() - millis >= timeout) {
return false;
}
private static final String cmd = "enabled=$(settings get secure enabled_accessibility_services)\n" +
"pkg=%s\n" +
"if [[ $enabled == *$pkg* ]]\n" +
"then\n" +
"echo already_enabled\n" +
"else\n" +
"enabled=$pkg:$enabled\n" +
"settings put secure enabled_accessibility_services $enabled\n" +
"fi";
public static boolean enableAccessibilityServiceByRoot(Class<? extends AccessibilityService> accessibilityService) {
String serviceName = App.getApp().getPackageName() + "/" + accessibilityService.getName();
return TextUtils.isEmpty(Shell.execCommand(String.format(Locale.getDefault(), cmd, serviceName), true).error);
}
public static boolean enableAccessibilityServiceByRootAndWaitFor(long timeOut) {
if (enableAccessibilityServiceByRoot(AccessibilityWatchDogService.class)) {
AccessibilityWatchDogService.waitForEnabled(timeOut);
}
return true;
}
public static void enableAccessibilityServiceByRootIfNeeded() {
if (AccessibilityWatchDogService.getInstance() == null)
if (Pref.def().getBoolean(App.getApp().getString(R.string.key_enable_accessibility_service_by_root), false)) {
AccessibilityServiceTool.enableAccessibilityServiceByRootAndWaitFor(AccessibilityWatchDogService.class, 3000);
if (Pref.enableAccessibilityServiceByRoot()) {
AccessibilityServiceTool.enableAccessibilityServiceByRoot(AccessibilityWatchDogService.class);
}
}
}

View File

@@ -0,0 +1,150 @@
package com.stardust.scriptdroid.tool;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.android.volley.DefaultRetryPolicy;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.stardust.pio.UncheckedIOException;
import java.lang.reflect.Type;
import java.util.List;
/**
* Created by Stardust on 2017/4/9.
*/
public class UpdateChecker implements Response.Listener<String>, Response.ErrorListener {
private static final String LOG_TAG = UpdateChecker.class.getName();
private RequestQueue mRequestQueue;
private static final String UPDATE_URL = "https://raw.githubusercontent.com/hyb1996/NoRootScriptDroid/master/version.json";
public interface Callback {
void onSuccess(CheckResult result);
void onError(Exception exception);
}
private Callback mCallback;
private final int mTimeOut = 3000;
public UpdateChecker(Context context, Callback callback) {
mCallback = callback;
mRequestQueue = Volley.newRequestQueue(context);
}
private CheckResult parse(String json) {
Gson gson = new Gson();
Type type = new TypeToken<CheckResult>() {
}.getType();
CheckResult result = gson.fromJson(json, type);
Log.i(LOG_TAG, result.toString());
return result;
}
public void check() {
StringRequest request = new StringRequest(Request.Method.GET, UPDATE_URL, this, this);
request.setRetryPolicy(new DefaultRetryPolicy(mTimeOut, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
request.setTag("update-check");
mRequestQueue.add(request);
}
@Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
mCallback.onError(error);
}
@Override
public void onResponse(String response) {
try {
CheckResult result = parse(response);
if (result.isValid()) {
mCallback.onSuccess(result);
} else {
mCallback.onError(new UpdateDataInvalidException());
}
} catch (Exception e) {
e.printStackTrace();
mCallback.onError(e);
}
}
public void cancel() {
mRequestQueue.cancelAll("update-check");
}
public static class CheckResult {
public int versionCode;
public String releaseNotes;
public String versionName;
public List<Download> downloads;
public List<OldVersion> oldVersions;
public int deprecated;
public boolean isValid() {
return downloads != null && !downloads.isEmpty() && versionCode > 0
&& !TextUtils.isEmpty(versionName) && !TextUtils.isEmpty(releaseNotes);
}
@Override
public String toString() {
return "CheckResult{" +
"versionCode=" + versionCode +
", releaseNotes='" + releaseNotes + '\'' +
", versionName='" + versionName + '\'' +
", downloads=" + downloads +
", oldVersions=" + oldVersions +
", deprecated=" + deprecated +
'}';
}
}
public static class OldVersion {
public int versionCode;
public String issues;
@Override
public String toString() {
return "OldVersion{" +
"versionCode=" + versionCode +
", issues='" + issues + '\'' +
'}';
}
}
public static class Download {
public String name;
public String url;
@Override
public String toString() {
return "Download{" +
"name='" + name + '\'' +
", url='" + url + '\'' +
'}';
}
}
public static class UpdateDataInvalidException extends RuntimeException {
}
}

View File

@@ -84,8 +84,6 @@ public class EditActivity extends Editor920Activity {
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";
@@ -173,14 +171,13 @@ public class EditActivity extends Editor920Activity {
}
private void openDrawerIfFirstUse() {
if (Pref.def().getBoolean(KEY_EDIT_ACTIVITY_FIRST_USE, true)) {
if (Pref.isEditActivityFirstUsing()) {
mDrawerLayout.postDelayed(new Runnable() {
@Override
public void run() {
mDrawerLayout.openDrawer(GravityCompat.END);
}
}, 1000);
Pref.def().edit().putBoolean(KEY_EDIT_ACTIVITY_FIRST_USE, false).apply();
}
}

View File

@@ -45,6 +45,7 @@ public class MyScriptListFragment extends Fragment {
private ScriptFile mSelectedScriptFile;
private MaterialDialog.InputCallback mFileNameInputCallback = new InputCallback(false);
private MaterialDialog.InputCallback mDirectoryNameInputCallback = new InputCallback(true);
private int mSelectedPosition;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -82,14 +83,15 @@ public class MyScriptListFragment extends Fragment {
});
mScriptListRecyclerView.setOnItemClickListener(new ScriptAndFolderListRecyclerView.OnScriptFileClickListener() {
@Override
public void onClick(ScriptFile file) {
public void onClick(ScriptFile file, int position) {
EditActivity.editFile(getContext(), file);
}
});
mScriptListRecyclerView.setOnItemLongClickListener(new ScriptAndFolderListRecyclerView.OnScriptFileLongClickListener() {
@Override
public void onLongClick(ScriptFile file) {
public void onLongClick(ScriptFile file, int position) {
mSelectedScriptFile = file;
mSelectedPosition = position;
if (file.isDirectory()) {
mDirectoryOperationDialog.show();
} else {
@@ -226,6 +228,7 @@ public class MyScriptListFragment extends Fragment {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
mSelectedScriptFile.renameTo(input.toString());
StorageScriptProvider.getDefault().notifyDirectoryChanged(mScriptListRecyclerView.getCurrentDirectory());
onScriptFileOperated();
}
});

View File

@@ -4,13 +4,13 @@ import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.support.annotation.Nullable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.workground.WrapContentLinearLayoutManager;
@@ -20,8 +20,11 @@ import com.stardust.scriptdroid.autojs.AutoJs;
import com.stardust.scriptdroid.scripts.ScriptFile;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.scripts.StorageScriptProvider;
import com.stardust.widget.ViewHolderMutableAdapter;
import com.stardust.widget.ViewHolderSupplier;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
import org.greenrobot.eventbus.Subscribe;
/**
@@ -32,12 +35,12 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
public interface OnScriptFileClickListener {
void onClick(ScriptFile file);
void onClick(ScriptFile file, int position);
}
public interface OnScriptFileLongClickListener {
void onLongClick(ScriptFile file);
void onLongClick(ScriptFile file, int position);
}
public interface FileProcessListener {
@@ -47,6 +50,28 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
void onFileListed();
}
public static final int VIEW_TYPE_DIRECTORY = 1;
public static final int VIEW_TYPE_FILE = 2;
public static class ViewHolder extends RecyclerView.ViewHolder {
TextView name;
public ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
}
public void bind(ScriptFile file) {
if (file.isDirectory()) {
name.setText(file.getName());
} else {
name.setText(file.getSimplifiedName());
}
}
}
private OnScriptFileClickListener mOnItemClickListener;
private OnScriptFileLongClickListener mOnItemLongClickListener;
private final OnClickListener mOnItemClickListenerProxy = new OnClickListener() {
@@ -57,11 +82,11 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
goBack();
return;
}
ScriptFile file = mScriptFileList[getActualPosition(position)];
ScriptFile file = mAdapter.getScriptFileAt(position);
if (file.isDirectory()) {
setCurrentDirectory(file, true);
} else if (mOnItemClickListener != null) {
mOnItemClickListener.onClick(file);
mOnItemClickListener.onClick(file, position);
}
}
};
@@ -70,7 +95,7 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
int position = getChildViewHolder(v).getAdapterPosition();
mOnItemLongClickListener.onLongClick(mScriptFileList[getActualPosition(position)]);
mOnItemLongClickListener.onLongClick(mAdapter.getScriptFileAt(position), position);
return true;
}
return false;
@@ -81,12 +106,23 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
@Override
public void onClick(View v) {
int position = getChildViewHolder((View) v.getParent()).getAdapterPosition();
ScriptFile file = mScriptFileList[getActualPosition(position)];
ScriptFile file = mAdapter.getScriptFileAt(position);
AutoJs.getInstance().getScriptEngineService().execute(new FileScriptSource(file));
}
};
private final ViewHolderSupplier<ViewHolder> mDefaultViewHolderSupplier = new ViewHolderSupplier<ViewHolder>() {
@Override
public ViewHolder createViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_FILE:
return new FileViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.script_and_folder_list_recycler_view_file, parent, false));
case VIEW_TYPE_DIRECTORY:
return new DefaultViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.script_and_folder_list_recycler_view_directory, parent, false));
}
return null;
}
};
private ScriptFile[] mScriptFileList = new ScriptFile[0];
private ScriptFile mCurrentDirectory;
private ScriptFile mRootDirectory;
private Adapter mAdapter;
@@ -119,12 +155,13 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
new Thread(new Runnable() {
@Override
public void run() {
mScriptFileList = mStorageScriptProvider.getDirectoryScriptFiles(directory);
final ScriptFile[] scriptFiles = mStorageScriptProvider.getDirectoryScriptFiles(directory);
post(new Runnable() {
@Override
public void run() {
mFileProcessListener.onFileListed();
mAdapter.notifyDataSetChanged();
mAdapter.setScripts(scriptFiles);
if (mFileProcessListener != null)
mFileProcessListener.onFileListed();
}
});
}
@@ -135,9 +172,8 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
setCurrentDirectory(directory, !directory.equals(mRootDirectory));
}
private void setRootDirectory(ScriptFile folder) {
mRootDirectory = folder;
setCurrentDirectory(mRootDirectory, false);
private void setRootDirectory(ScriptFile directory) {
mRootDirectory = directory;
}
public void setFileProcessListener(FileProcessListener fileProcessListener) {
@@ -168,6 +204,18 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
mScriptFileOperationEnabled = scriptFileOperationEnabled;
}
public void setViewHolderSupplier(ViewHolderSupplier<ViewHolder> supplier) {
mAdapter.setViewHolderSupplier(supplier);
}
public OnClickListener getOnItemClickListenerProxy() {
return mOnItemClickListenerProxy;
}
public OnLongClickListener getOnItemLongClickListenerProxy() {
return mOnItemLongClickListenerProxy;
}
private void goBack() {
ScriptFile parent = mCurrentDirectory.getParentFile();
setCurrentDirectory(parent, !parent.equals(mRootDirectory));
@@ -181,10 +229,11 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
.marginResId(R.dimen.script_and_folder_list_divider_left_margin, R.dimen.script_and_folder_list_divider_right_margin)
.showLastDivider()
.build());
mAdapter = new Adapter();
mAdapter = new Adapter(mDefaultViewHolderSupplier);
setAdapter(mAdapter);
}
@Override
protected Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
@@ -208,6 +257,7 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
super.onAttachedToWindow();
if (mStorageScriptProvider != null)
mStorageScriptProvider.registerDirectoryChangeListener(this);
}
@Override
@@ -234,24 +284,28 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
}
}
@Override
public Adapter getAdapter() {
return mAdapter;
}
private void updateCurrentDirectory() {
setCurrentDirectory(mCurrentDirectory, mCanGoBack);
}
private class Adapter extends RecyclerView.Adapter<ViewHolder> {
private void ensureCurrentDirectory() {
if (mCurrentDirectory == null && mRootDirectory != null) {
setCurrentDirectory(mRootDirectory, false);
}
}
private final int VIEW_TYPE_FOLDER = 1;
private final int VIEW_TYPE_FILE = 2;
public class Adapter extends ViewHolderMutableAdapter<ViewHolder> {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case VIEW_TYPE_FILE:
return new FileViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.script_and_folder_list_recycler_view_file, parent, false));
case VIEW_TYPE_FOLDER:
return new ViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.script_and_folder_list_recycler_view_directory, parent, false));
}
return null;
private ScriptFile[] mScriptFileList = new ScriptFile[0];
public Adapter(ViewHolderSupplier<ViewHolder> viewHolderSupplier) {
super(viewHolderSupplier);
}
@Override
@@ -259,37 +313,41 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
if (mCanGoBack && position == 0) {
holder.name.setText("..");
} else
holder.bind(mScriptFileList[getActualPosition(position)]);
holder.bind(getScriptFileAt(position));
}
@Override
public int getItemCount() {
ensureCurrentDirectory();
return mScriptFileList.length + (mCanGoBack ? 1 : 0);
}
@Override
public int getItemViewType(int position) {
if (mCanGoBack && position == 0) {
return VIEW_TYPE_FOLDER;
return VIEW_TYPE_DIRECTORY;
}
return mScriptFileList[getActualPosition(position)].isDirectory() ? VIEW_TYPE_FOLDER : VIEW_TYPE_FILE;
return getScriptFileAt(position).isDirectory() ? VIEW_TYPE_DIRECTORY : VIEW_TYPE_FILE;
}
public ScriptFile getScriptFileAt(int position) {
return mScriptFileList[mCanGoBack ? position - 1 : position];
}
void setScripts(ScriptFile[] scriptFiles) {
mScriptFileList = scriptFiles;
notifyDataSetChanged();
}
}
private int getActualPosition(int position) {
return mCanGoBack ? position - 1 : position;
}
private class ViewHolder extends RecyclerView.ViewHolder {
private class DefaultViewHolder extends ViewHolder {
TextView name;
ViewHolder(View itemView) {
DefaultViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(mOnItemClickListenerProxy);
itemView.setOnLongClickListener(mOnItemLongClickListenerProxy);
name = (TextView) itemView.findViewById(R.id.name);
if (!mScriptFileOperationEnabled) {
setMarginRight(name, 0);
}
@@ -301,16 +359,9 @@ public class ScriptAndFolderListRecyclerView extends RecyclerView {
view.setLayoutParams(layoutParams);
}
public void bind(ScriptFile file) {
if (file.isDirectory()) {
name.setText(file.getName());
} else {
name.setText(file.getSimplifiedName());
}
}
}
private class FileViewHolder extends ViewHolder {
private class FileViewHolder extends DefaultViewHolder {
FileViewHolder(View itemView) {
super(itemView);

View File

@@ -52,7 +52,7 @@ public class ScriptFileChooserDialogBuilder extends MaterialDialog.Builder {
if (mFileCallback != null) {
mScriptListWithProgressBarView.setOnItemClickListener(new ScriptAndFolderListRecyclerView.OnScriptFileClickListener() {
@Override
public void onClick(ScriptFile file) {
public void onClick(ScriptFile file, int position) {
mFileCallback.onFileSelection(dialog, file);
}
});

View File

@@ -128,7 +128,7 @@ public class TaskListRecyclerView extends ThemeColorRecyclerView implements Java
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.bind(mScriptEngines.get(position).getExecutedScript());
holder.bind((ScriptSource) mScriptEngines.get(position).getTag("script"));
}
@Override

View File

@@ -46,10 +46,10 @@ public class TaskManagerFragment extends Fragment {
@Override
public void onSomethingChanged() {
final boolean noRunningScript = mTaskListRecyclerView.getAdapter().getItemCount() == 0;
mNoRunningScriptNotice.setVisibility(noRunningScript ? View.VISIBLE : View.GONE);
mCloseAllView.postDelayed(new Runnable() {
@Override
public void run() {
mNoRunningScriptNotice.setVisibility(noRunningScript ? View.VISIBLE : View.GONE);
mCloseAllView.setVisibility(noRunningScript ? View.GONE : View.VISIBLE);
}
}, 150);

View File

@@ -11,9 +11,11 @@ import android.view.View;
import android.widget.Toast;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.tool.UpdateChecker;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.ui.error.IssueReporterActivity;
import com.stardust.scriptdroid.ui.main.MainActivity;
import com.stardust.scriptdroid.ui.update.UpdateCheckDialog;
import com.stardust.util.IntentUtil;
import com.stardust.util.MapEntries;
import com.stardust.scriptdroid.R;
@@ -115,6 +117,12 @@ public class SettingsActivity extends BaseActivity {
EventBus.getDefault().post(new MessageEvent(MainActivity.MESSAGE_CLEAR_BACKGROUND_SETTINGS));
}
})
.entry(getString(R.string.text_check_update), new Runnable() {
@Override
public void run() {
new UpdateCheckDialog(getActivity()).show();
}
})
.entry(getString(R.string.text_issue_report), new Runnable() {
@Override
public void run() {

View File

@@ -0,0 +1,98 @@
package com.stardust.scriptdroid.ui.update;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.widget.Toast;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.BuildConfig;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.tool.UpdateChecker;
import com.stardust.util.IntentUtil;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by Stardust on 2017/4/9.
*/
public class UpdateCheckDialog implements UpdateChecker.Callback {
private MaterialDialog mResultDialog;
private MaterialDialog mProgressDialog;
private Context mContext;
private UpdateChecker mUpdateChecker;
public UpdateCheckDialog(Context context) {
mContext = context;
mUpdateChecker = new UpdateChecker(context, this);
}
public void show() {
mProgressDialog = new MaterialDialog.Builder(mContext)
.content(mContext.getString(R.string.text_checking_update))
.progress(true, 0)
.cancelable(false)
.show();
mUpdateChecker.check();
}
@Override
public void onSuccess(UpdateChecker.CheckResult result) {
mProgressDialog.dismiss();
if (BuildConfig.VERSION_CODE >= result.versionCode) {
Toast.makeText(mContext, R.string.text_is_latest_version, Toast.LENGTH_SHORT).show();
return;
}
mResultDialog = buildDialog(result);
mResultDialog.show();
String updateSite = result.downloads.get(0).name;
final String updateUrl = result.downloads.get(0).url;
mResultDialog = new MaterialDialog.Builder(mContext)
.title(mContext.getString(R.string.text_new_version) + " " + result.versionName)
.content(result.releaseNotes)
.positiveText(updateSite)
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
IntentUtil.browse(mContext, updateUrl);
}
})
.show();
}
private MaterialDialog buildDialog(final UpdateChecker.CheckResult result) {
String updateSite = result.downloads.get(0).name;
final String updateUrl = result.downloads.get(0).url;
MaterialDialog.Builder builder = new MaterialDialog.Builder(mContext)
.title(mContext.getString(R.string.text_new_version) + " " + result.versionName)
.content(result.releaseNotes)
.positiveText(updateSite)
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
IntentUtil.browse(mContext, updateUrl);
}
});
if (result.downloads.size() > 1) {
builder.negativeText(result.downloads.get(1).name)
.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
IntentUtil.browse(mContext, result.downloads.get(1).url);
}
});
}
return builder.build();
}
@Override
public void onError(Exception exception) {
mProgressDialog.dismiss();
Toast.makeText(mContext, R.string.text_check_update_error, Toast.LENGTH_SHORT).show();
}
}

View File

@@ -0,0 +1,18 @@
package com.stardust.widget;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
* Created by Stardust on 2017/4/8.
*/
public abstract class BindableViewHolder<DataType> extends RecyclerView.ViewHolder{
public BindableViewHolder(View itemView) {
super(itemView);
}
public abstract void bind(DataType data);
}

View File

@@ -0,0 +1,39 @@
package com.stardust.widget;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
/**
* Created by Stardust on 2017/4/6.
*/
public class ItemTouchHelperSimpleCallback extends ItemTouchHelper.SimpleCallback {
private boolean mLongPressDragEnabled, mItemViewSwipeEnabled;
public ItemTouchHelperSimpleCallback(int dragDirs, int swipeDirs) {
super(dragDirs, swipeDirs);
mItemViewSwipeEnabled = swipeDirs != 0;
mLongPressDragEnabled = dragDirs != 0;
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
}
@Override
public boolean isLongPressDragEnabled() {
return mLongPressDragEnabled;
}
@Override
public boolean isItemViewSwipeEnabled() {
return mItemViewSwipeEnabled;
}
}

View File

@@ -0,0 +1,28 @@
package com.stardust.widget;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
/**
* Created by Stardust on 2017/4/8.
*/
public abstract class ViewHolderMutableAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {
private ViewHolderSupplier<VH> mViewHolderSupplier;
public ViewHolderMutableAdapter(ViewHolderSupplier<VH> viewHolderSupplier) {
mViewHolderSupplier = viewHolderSupplier;
}
@Override
public VH onCreateViewHolder(ViewGroup parent, int viewType) {
return mViewHolderSupplier.createViewHolder(parent, viewType);
}
public void setViewHolderSupplier(ViewHolderSupplier<VH> viewHolderSupplier) {
mViewHolderSupplier = viewHolderSupplier;
notifyDataSetChanged();
}
}

View File

@@ -0,0 +1,14 @@
package com.stardust.widget;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
/**
* Created by Stardust on 2017/4/8.
*/
public abstract class ViewHolderSupplier<VH extends RecyclerView.ViewHolder> {
public abstract VH createViewHolder(ViewGroup parent, int viewType);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:selectableItemBackground">
<ImageView
android:id="@+id/icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginBottom="4dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:contentDescription="@string/_app_name"
android:scaleType="fitCenter"
android:src="@drawable/ic_folder_yellow_100px"/>
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="80dp"
android:layout_toRightOf="@id/icon"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"
tools:text="示例脚本"/>
</RelativeLayout>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:selectableItemBackground"
tools:background="@color/colorPrimary">
<ImageView
android:id="@+id/icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_marginBottom="4dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:contentDescription="@string/_app_name"
android:scaleType="fitCenter"
android:src="@drawable/ic_node_js_black"/>
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
android:layout_marginRight="54dp"
android:layout_toRightOf="@id/icon"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"
tools:text="正在运行的服务"/>
<LinearLayout
android:id="@+id/edit"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="12dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center"
android:contentDescription="@string/_app_name"
android:src="@drawable/ic_edit_white"/>
</LinearLayout>
</RelativeLayout>

View File

@@ -1,78 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:foreground="?android:attr/selectableItemBackground"
android:orientation="vertical"
android:paddingBottom="12dp"
android:paddingLeft="16dp"
android:paddingTop="12dp"
tools:background="@color/colorPrimary">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_marginRight="80dp"
android:orientation="vertical">
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:textColor="@android:color/primary_text_dark"
android:textSize="18sp"
tools:text="正在运行的服务"/>
<TextView
android:id="@+id/path"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:ellipsize="end"
android:maxLines="1"
android:textColor="#e4e4e4"
android:textSize="14sp"
tools:text="/storage/emulated/0/脚本/正在运行的服务.txt"/>
</LinearLayout>
<FrameLayout
android:id="@+id/edit"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="42dp"
android:background="?attr/selectableItemBackgroundBorderless">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:contentDescription="@string/_app_name"
android:src="@drawable/ic_edit_white"/>
</FrameLayout>
<FrameLayout
android:id="@+id/more"
android:layout_width="42dp"
android:layout_height="42dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:background="?attr/selectableItemBackgroundBorderless">
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:contentDescription="@string/_app_name"
android:src="@drawable/more_three_dots_ios_7_white"/>
</FrameLayout>
</RelativeLayout>

View File

@@ -158,6 +158,9 @@
<string name="text_please_choose_file_to_import">请选择要导入的脚本</string>
<string name="text_refresh">刷新</string>
<string name="notice_no_running_script"><![CDATA[没有正在运行的脚本 φ(>ω<*)]]></string>
<string name="text_checking_update">正在检查更新</string>
<string name="text_new_version">新版本</string>
<string name="text_is_latest_version">已经是最新版本</string>
<string-array name="record_control_keys">
<item>None</item>
<item>Volume Up</item>

View File

@@ -194,6 +194,11 @@
<string name="text_clear_pre_execute_script">清空预执行脚本</string>
<string name="text_clear_file_selection">重置脚本文件选择</string>
<string name="summary_pre_execute_script">将在脚本文件执行前执行</string>
<string name="text_checking_update">正在检查更新</string>
<string name="text_new_version">新版本</string>
<string name="text_check_update_error">检查更新失败Orz</string>
<string name="text_check_update">检查更新</string>
<string name="text_is_latest_version">已经是最新版本</string>
<string-array name="record_control_keys">
<item>无</item>

View File

@@ -67,13 +67,15 @@
android:entries="@array/run_mode_keys"
android:entryValues="@array/run_mode_values"
android:key="@string/key_run_mode"
android:title="@string/text_command_run_mode"
android:summary="@string/summary_command_run_mode"
android:title="@string/text_command_run_mode"
/>
</com.stardust.theme.preference.ThemeColorPreferenceCategory>
<com.stardust.theme.preference.ThemeColorPreferenceCategory android:title="@string/text_about">
<Preference android:title="@string/text_check_update"/>
<Preference android:title="@string/text_issue_report"/>
<Preference android:title="@string/text_join_qq_group"/>

View File

@@ -1,5 +1,5 @@
if(__engine__ == "rhino"){
if(__engine_name__ == "rhino"){
__importClassOld__ = importClass;
var importClass = function(pack){
if(typeof(pack) == "string"){
@@ -167,9 +167,9 @@ var scrollDown = function(a, b, c, d){
var input = function(a, b){
if(arguments.length == 1){
return __runtime__.automator.setText(__runtime__.editable(-1), a);
return __runtime__.automator.setText(__runtime__.automator.editable(-1), a);
}else{
return __runtime__.automator.setText(__runtime__.editable(a), b);
return __runtime__.automator.setText(__runtime__.automator.editable(a), b);
}
}
@@ -246,14 +246,18 @@ var Text = function(text){
return shell("input text " + text, true).code == 1;
}
var __selector__ = __runtime__.selector();
var selector = function(){
return __runtime__.selector(__engine__);
}
var __selector__ = selector();
var __obj__ = new java.lang.Object();
for(var x in __selector__){
if(!__obj__[x] && !this[x]){
this[x] = (function(method) {
return function(){
var s = __runtime__.selector();
var s = selector();
//这里不知道怎么写。尴尬。只能写成这样。
if(arguments.length == 0){
return s[method]();
@@ -274,8 +278,16 @@ for(var x in __selector__){
}
var open = function(path, mode){
return com.stardust.pio.PFile.open(path, mode);
var open = function(path, mode, encoding, bufferSize){
if(arguments.length == 1){
return com.stardust.pio.PFile.open(path);
}else if(arguments.length == 2){
return com.stardust.pio.PFile.open(path, mode);
}else if(arguments.length == 3){
return com.stardust.pio.PFile.open(path, mode, encoding);
}else if(arguments.length == 4){
return com.stardust.pio.PFile.open(path, mode, encoding, bufferSize);
}
}
__importClassOld__(com.stardust.autojs.runtime.api.Shell);

View File

@@ -1,6 +1,7 @@
package com.stardust.autojs;
import android.content.Context;
import android.util.Log;
import com.stardust.autojs.engine.JavaScriptEngine;
import com.stardust.autojs.engine.JavaScriptEngineManager;
@@ -10,6 +11,7 @@ import com.stardust.autojs.runtime.api.Console;
import com.stardust.autojs.script.ScriptSource;
import java.text.DateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.Set;
@@ -20,6 +22,7 @@ import java.util.Set;
public class ScriptEngineService {
private static final String LOG_TAG = "ScriptEngineService";
private static ScriptEngineService instance;
public static ScriptEngineService getInstance() {
@@ -73,7 +76,9 @@ public class ScriptEngineService {
}
public JavaScriptEngine createScriptEngine() {
return mJavaScriptEngineManager.createEngine();
JavaScriptEngine engine = mJavaScriptEngineManager.createEngine();
Log.i(LOG_TAG, Arrays.toString(Thread.currentThread().getStackTrace()));
return engine;
}
public void registerEngineLifecycleCallback(JavaScriptEngineManager.EngineLifecycleCallback engineLifecycleCallback) {

View File

@@ -40,6 +40,7 @@ public class ScriptExecutionTask implements Serializable {
runtime.ensureAccessibilityServiceEnabled();
}
mExecutionListener.onStart(engine, mScriptSource);
engine.setTag("script", mScriptSource);
mExecutionListener.onSuccess(engine, mScriptSource, engine.execute(mScriptSource));
} catch (Exception e) {
mExecutionListener.onException(engine, mScriptSource, e);

View File

@@ -21,12 +21,14 @@ public interface JavaScriptEngine {
Object execute(ScriptSource scriptSource);
ScriptSource getExecutedScript();
void forceStop();
void destroy();
void setTag(String key, Object value);
Object getTag(String key);
/**
* @hide
*/

View File

@@ -44,13 +44,17 @@ public abstract class JavaScriptEngineManager {
JavaScriptEngine engine = createEngineInner();
putProperties(engine);
engine.init();
addEngine(engine);
return engine;
}
private void addEngine(JavaScriptEngine engine) {
synchronized (mEngines) {
mEngines.add(engine);
if(mEngineLifecycleCallback != null){
if (mEngineLifecycleCallback != null) {
mEngineLifecycleCallback.onEngineCreate(engine);
}
}
return engine;
}
public void put(String varName, Object value) {
@@ -82,7 +86,7 @@ public abstract class JavaScriptEngineManager {
void removeEngine(JavaScriptEngine engine) {
synchronized (mEngines) {
mEngines.remove(engine);
if(mEngineLifecycleCallback != null){
if (mEngineLifecycleCallback != null) {
mEngineLifecycleCallback.onEngineRemove(engine);
}
}

View File

@@ -1,9 +1,13 @@
package com.stardust.autojs.engine;
import android.util.Log;
import com.stardust.autojs.rhino_android.AndroidContextFactory;
import com.stardust.autojs.rhino_android.RhinoAndroidHelper;
import com.stardust.autojs.runtime.ScriptRuntime;
import com.stardust.autojs.runtime.ScriptStopException;
import com.stardust.autojs.script.ScriptSource;
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
@@ -12,6 +16,12 @@ import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
/**
* Created by Stardust on 2017/4/2.
@@ -19,17 +29,20 @@ import java.io.File;
public class RhinoJavaScriptEngine implements JavaScriptEngine {
private static final String LOG_TAG = "RhinoJavaScriptEngine";
private Context mContext;
private Scriptable mScriptable;
private Thread mThread;
private RhinoJavaScriptEngineManager mEngineManager;
private ScriptSource mScriptSource;
private Map<String, Object> mTags = new Hashtable<>();
public RhinoJavaScriptEngine(RhinoJavaScriptEngineManager engineManager) {
mEngineManager = engineManager;
mThread = Thread.currentThread();
mContext = createContext();
mScriptable = createScope(mContext);
setTag("create-traces", Arrays.toString(Thread.currentThread().getStackTrace()));
}
@Override
@@ -39,15 +52,11 @@ public class RhinoJavaScriptEngine implements JavaScriptEngine {
@Override
public Object execute(ScriptSource source) {
mScriptSource = source;
setTag("execute-traces", Arrays.toString(Thread.currentThread().getStackTrace()));
setTag("execute-source", source);
return mContext.evaluateString(mScriptable, source.getScript(), "<script>", 1, null);
}
@Override
public ScriptSource getExecutedScript() {
return mScriptSource;
}
@Override
public void forceStop() {
mThread.interrupt();
@@ -60,12 +69,37 @@ public class RhinoJavaScriptEngine implements JavaScriptEngine {
@Override
public void destroy() {
Context.exit();
// TODO: 2017/4/6 XXX :在这里回收内存池并不好
final AccessibilityNodeInfoAllocator allocator = (AccessibilityNodeInfoAllocator) getTag("allocator");
if (allocator != null) {
new Timer().schedule(new TimerTask() {
@Override
public void run() {
//allocator.recycleAll();
}
}, 1000);
}
mEngineManager.removeEngine(this);
}
@Override
public synchronized void setTag(String key, Object value) {
mTags.put(key, value);
}
@Override
public synchronized Object getTag(String key) {
Object tag = mTags.get(key);
if (tag == null && key.equals("script")) {
Log.i(LOG_TAG, mTags.entrySet().toString());
}
return mTags.get(key);
}
@Override
public void init() {
ScriptableObject.putProperty(mScriptable, "__engine__", "rhino");
ScriptableObject.putProperty(mScriptable, "__engine_name__", "rhino");
ScriptableObject.putProperty(mScriptable, "__engine__", this);
mContext.evaluateString(mScriptable, mEngineManager.getInitScript().getScript(), "<init>", 1, null);
}

View File

@@ -31,6 +31,7 @@ public class RhinoJavaScriptEngineManager extends JavaScriptEngineManager {
private String mRequirePath = "";
public RhinoJavaScriptEngineManager(android.content.Context context, ScriptRuntime runtime) {
super(context);
put("__runtime__", runtime);
@@ -65,7 +66,9 @@ public class RhinoJavaScriptEngineManager extends JavaScriptEngineManager {
private String[] getGlobalFunctionsInner() {
JavaScriptEngine engine = createEngine();
Scriptable scriptable = (Scriptable) engine.execute(new StringScriptSource("this"));
ScriptSource source = new StringScriptSource("this", "this");
engine.setTag("script", source);
Scriptable scriptable = (Scriptable) engine.execute(source);
Object[] ids = scriptable.getIds();
String[] functions = new String[ids.length];
for (int i = 0; i < ids.length; i++) {

View File

@@ -46,6 +46,7 @@ public class ScriptExecuteActivity extends Activity {
private void runScript() {
mJavaScriptEngine.put("activity", this);
try {
mJavaScriptEngine.setTag("script", mScriptSource);
mResult = mJavaScriptEngine.execute(mScriptSource);
} catch (Exception e) {
mExecutionListener.onException(mJavaScriptEngine, mScriptSource, e);

View File

@@ -6,6 +6,7 @@ import android.os.Handler;
import android.widget.Toast;
import com.stardust.autojs.R;
import com.stardust.autojs.engine.JavaScriptEngine;
import com.stardust.autojs.rhino_android.AndroidClassLoader;
import com.stardust.autojs.runtime.simple_action.SimpleActionAutomator;
import com.stardust.autojs.runtime.api.AppUtils;
@@ -16,6 +17,7 @@ import com.stardust.util.ClipboardUtil;
import com.stardust.util.SdkVersionUtil;
import com.stardust.util.Shell;
import com.stardust.view.accessibility.AccessibilityInfoProvider;
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
import org.mozilla.javascript.ContextFactory;
@@ -92,8 +94,13 @@ public class ScriptRuntime {
}
@JavascriptInterface
public UiSelector selector() {
return new UiSelector(mAccessibilityBridge);
public UiSelector selector(JavaScriptEngine engine) {
AccessibilityNodeInfoAllocator allocator = (AccessibilityNodeInfoAllocator) engine.getTag("allocator");
if(allocator == null){
allocator = new AccessibilityNodeInfoAllocator();
engine.setTag("allocator", allocator);
}
return new UiSelector(mAccessibilityBridge, allocator);
}
@JavascriptInterface

View File

@@ -7,12 +7,14 @@ import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.autojs.runtime.AccessibilityBridge;
import com.stardust.autojs.runtime.JavascriptInterface;
import com.stardust.autojs.runtime.ScriptStopException;
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.util.DeveloperUtils;
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
@@ -53,6 +55,7 @@ import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.
public class UiSelector extends UiGlobalSelector {
@Deprecated
private class FindCommand extends AccessibilityEventCommandHost.AbstractCommand {
UiObjectCollection result;
@@ -70,20 +73,28 @@ public class UiSelector extends UiGlobalSelector {
private static final String TAG = "UiSelector";
private AccessibilityBridge mAccessibilityBridge;
private AccessibilityNodeInfoAllocator mAllocator = AccessibilityNodeInfoAllocator.getGlobal();
public UiSelector(AccessibilityBridge accessibilityBridge) {
mAccessibilityBridge = accessibilityBridge;
}
public UiSelector(AccessibilityBridge accessibilityBridge, AccessibilityNodeInfoAllocator allocator) {
mAccessibilityBridge = accessibilityBridge;
mAllocator = allocator;
}
@JavascriptInterface
public UiObjectCollection find() {
ensureAccessibilityServiceEnabled();
if (isRunningPackageSelf()) {
return null;
}
AccessibilityService service = mAccessibilityBridge.getService();
if (service != null) {
AccessibilityNodeInfo root = service.getRootInActiveWindow();
if (root != null) {
return findOf(root);
return findOf(mAllocator, root);
}
}
return null;
@@ -93,12 +104,19 @@ public class UiSelector extends UiGlobalSelector {
mAccessibilityBridge.ensureServiceEnabled();
}
private boolean isRunningPackageSelf() {
return DeveloperUtils.isRunningPackageSelf(mAccessibilityBridge.getInfoProvider().getLatestPackage());
}
@JavascriptInterface
@NonNull
public UiObjectCollection untilFind() {
UiObjectCollection uiObjectCollection;
do {
if (Thread.currentThread().isInterrupted()) {
throw new ScriptStopException(new InterruptedException());
}
uiObjectCollection = find();
} while (uiObjectCollection == null || uiObjectCollection.size() == 0);
return uiObjectCollection;

View File

@@ -15,6 +15,8 @@ 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;
import com.stardust.util.DeveloperUtils;
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
/**
* Created by Stardust on 2017/4/2.
@@ -180,16 +182,26 @@ public class SimpleActionAutomator {
}
@SuppressWarnings("unchecked")
private <T> T performAction(SimpleAction simpleAction) {
private boolean performAction(SimpleAction simpleAction) {
ensureAccessibilityServiceEnabled();
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));
}
if (isRunningPackageSelf()) {
return false;
}
return (T) simpleAction.getResult();
AccessibilityService service = mAccessibilityBridge.getService();
if (service == null)
return false;
AccessibilityNodeInfo root = service.getRootInActiveWindow();
if (root == null)
return false;
AccessibilityNodeInfoAllocator allocator = new AccessibilityNodeInfoAllocator();
simpleAction.setAllocator(allocator);
Log.i("Automator", "performAction: " + simpleAction + " root = " + root);
boolean result = simpleAction.perform(root);
allocator.recycleAll();
return result;
}
private boolean isRunningPackageSelf() {
return DeveloperUtils.isRunningPackageSelf(mAccessibilityBridge.getInfoProvider().getLatestPackage());
}
}

View File

@@ -352,27 +352,26 @@ public class UiGlobalSelector {
return this;
}
public UiObjectCollection findOf(AccessibilityNodeInfo node) {
List<AccessibilityNodeInfo> list = new ArrayList<>();
list.add(node);
for (ListFilter filter : mFilters) {
list = filter.filter(list);
}
return UiObjectCollection.of(list);
}
public UiObjectCollection findOf(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo node) {
List<AccessibilityNodeInfo> list = new ArrayList<>();
list.add(node);
for (ListFilter filter : mFilters) {
list = filter.filter(list);
list = filter.filter(allocator, list);
}
return UiObjectCollection.of(list);
}
public UiObject findOneOf(AccessibilityNodeInfo node) {
public UiObjectCollection findOf(AccessibilityNodeInfo node) {
return findOf(AccessibilityNodeInfoAllocator.NONE, node);
}
public UiObject findOneOf(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo node) {
// TODO: 2017/3/9 优化
return new UiObject(findOf(node).get(0).getInfo());
return new UiObject(findOf(allocator, node).get(0).getInfo());
}
public UiObject findOneOf(AccessibilityNodeInfo node) {
return findOneOf(AccessibilityNodeInfoAllocator.NONE, node);
}
public UiGlobalSelector addFilter(ListFilter filter) {

View File

@@ -14,36 +14,36 @@ import java.util.List;
public abstract class DfsFilter implements ListFilter, Filter {
@Override
public List<AccessibilityNodeInfo> filter(List<AccessibilityNodeInfo> nodes) {
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, List<AccessibilityNodeInfo> nodes) {
ArrayList<AccessibilityNodeInfo> list = new ArrayList<>();
for (AccessibilityNodeInfo node : nodes) {
if (isIncluded(node)) {
list.add(node);
}
filterChildren(node, list);
filterChildren(allocator, node, list);
}
return list;
}
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo node) {
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo node) {
ArrayList<AccessibilityNodeInfo> list = new ArrayList<>();
if (isIncluded(node)) {
list.add(node);
}
filterChildren(node, list);
filterChildren(allocator, node, list);
return list;
}
private void filterChildren(AccessibilityNodeInfo parent, List<AccessibilityNodeInfo> list) {
private void filterChildren(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo parent, List<AccessibilityNodeInfo> list) {
for (int i = 0; i < parent.getChildCount(); i++) {
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(parent, i);
AccessibilityNodeInfo child = allocator.getChild(parent, i);
if (child == null)
continue;
boolean included = isIncluded(child);
if (included) {
list.add(child);
}
filterChildren(child, list);
filterChildren(allocator, child, list);
if (!included) {
child.recycle();
}

View File

@@ -2,6 +2,8 @@ package com.stardust.automator.filter;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
import java.util.List;
/**
@@ -10,6 +12,6 @@ import java.util.List;
public interface Filter {
List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo node);
List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo node);
}

View File

@@ -2,6 +2,8 @@ package com.stardust.automator.filter;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
import java.util.List;
/**
@@ -45,8 +47,8 @@ public class IdFilter extends ListFilter.Default {
}
@Override
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo node) {
return node.findAccessibilityNodeInfosByViewId(mId);
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo node) {
return allocator.findAccessibilityNodeInfosByViewId(node, mId);
}

View File

@@ -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;
@@ -11,15 +13,15 @@ import java.util.List;
public interface ListFilter {
List<AccessibilityNodeInfo> filter(List<AccessibilityNodeInfo> nodes);
List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, List<AccessibilityNodeInfo> nodes);
abstract class Default implements Filter, ListFilter {
@Override
public List<AccessibilityNodeInfo> filter(List<AccessibilityNodeInfo> nodes) {
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, List<AccessibilityNodeInfo> nodes) {
List<AccessibilityNodeInfo> list = new ArrayList<>();
for (AccessibilityNodeInfo node : nodes) {
list.addAll(filter(node));
list.addAll(filter(allocator, node));
}
return list;
}

View File

@@ -2,6 +2,8 @@ package com.stardust.automator.filter;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.view.accessibility.AccessibilityNodeInfoAllocator;
import java.util.List;
/**
@@ -46,7 +48,7 @@ public class TextFilter extends ListFilter.Default {
@Override
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo node) {
return node.findAccessibilityNodeInfosByText(mText);
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo node) {
return allocator.findAccessibilityNodeInfosByText(node, mText);
}
}

View File

@@ -12,7 +12,6 @@ public class DepthFirstSearchTargetAction extends SearchTargetAction {
private Able mAble;
private static final long TIME_LIMIT = 500;
public DepthFirstSearchTargetAction(int action, Filter filter) {
super(action, filter);
@@ -27,7 +26,7 @@ public class DepthFirstSearchTargetAction extends SearchTargetAction {
if (mAble.isAble(n))
return n;
for (int i = 0; i < n.getChildCount(); i++) {
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(n, i);
AccessibilityNodeInfo child = getAllocator().getChild(n, i);
if (child == null)
continue;
AccessibilityNodeInfo node = searchTarget(child);

View File

@@ -18,7 +18,7 @@ public abstract class FilterAction extends SimpleAction {
public interface Filter {
List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root);
List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo root);
}
public static class TextFilter implements Filter {
@@ -32,12 +32,12 @@ public abstract class FilterAction extends SimpleAction {
}
@Override
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root) {
List<AccessibilityNodeInfo> list = AccessibilityNodeInfoAllocator.getGlobal().findAccessibilityNodeInfosByText(root, mText);
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo root) {
List<AccessibilityNodeInfo> list = allocator.findAccessibilityNodeInfosByText(root, mText);
if (mIndex == -1)
return list;
if (mIndex >= list.size())
return Collections.EMPTY_LIST;
return Collections.emptyList();
return Collections.singletonList(list.get(mIndex));
}
@@ -59,11 +59,11 @@ public abstract class FilterAction extends SimpleAction {
}
@Override
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root) {
return Collections.singletonList(findAccessibilityNodeInfosByBounds(root));
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo root) {
return Collections.singletonList(findAccessibilityNodeInfosByBounds(allocator, root));
}
private AccessibilityNodeInfo findAccessibilityNodeInfosByBounds(AccessibilityNodeInfo root) {
private AccessibilityNodeInfo findAccessibilityNodeInfosByBounds(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo root) {
if (root == null)
return null;
Rect rect = new Rect();
@@ -72,10 +72,10 @@ public abstract class FilterAction extends SimpleAction {
return root;
}
for (int i = 0; i < root.getChildCount(); i++) {
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(root, i);
AccessibilityNodeInfo child = allocator.getChild(root, i);
if (child == null)
continue;
AccessibilityNodeInfo nodeInfo = findAccessibilityNodeInfosByBounds(child);
AccessibilityNodeInfo nodeInfo = findAccessibilityNodeInfosByBounds(allocator, child);
if (nodeInfo != null)
return nodeInfo;
else
@@ -101,16 +101,16 @@ public abstract class FilterAction extends SimpleAction {
}
@Override
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root) {
List<AccessibilityNodeInfo> editableList = findEditable(root);
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo root) {
List<AccessibilityNodeInfo> editableList = findEditable(allocator, root);
if (mIndex == -1)
return editableList;
if (mIndex >= editableList.size())
return Collections.EMPTY_LIST;
return Collections.emptyList();
return Collections.singletonList(editableList.get(mIndex));
}
public static List<AccessibilityNodeInfo> findEditable(AccessibilityNodeInfo root) {
public static List<AccessibilityNodeInfo> findEditable(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo root) {
if (root == null) {
return Collections.emptyList();
}
@@ -119,7 +119,7 @@ public abstract class FilterAction extends SimpleAction {
}
List<AccessibilityNodeInfo> list = new LinkedList<>();
for (int i = 0; i < root.getChildCount(); i++) {
list.addAll(findEditable(AccessibilityNodeInfoAllocator.getGlobal().getChild(root, i)));
list.addAll(findEditable(allocator, allocator.getChild(root, i)));
}
return list;
}
@@ -141,8 +141,8 @@ public abstract class FilterAction extends SimpleAction {
}
@Override
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfo root) {
return AccessibilityNodeInfoAllocator.getGlobal().findAccessibilityNodeInfosByViewId(root, mId);
public List<AccessibilityNodeInfo> filter(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo root) {
return allocator.findAccessibilityNodeInfosByViewId(root, mId);
}
@Override
@@ -163,7 +163,7 @@ public abstract class FilterAction extends SimpleAction {
public boolean perform(AccessibilityNodeInfo root) {
if (root == null)
return false;
List<AccessibilityNodeInfo> list = mFilter.filter(root);
List<AccessibilityNodeInfo> list = mFilter.filter(getAllocator(), root);
boolean succeed = perform(list);
AccessibilityNodeInfoAllocator.recycleList(root, list);
return succeed;

View File

@@ -1,44 +0,0 @@
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/14.
*/
public class GetTextAction extends SimpleAction {
@Override
public boolean perform(AccessibilityNodeInfo root) {
List<String> texts = new ArrayList<>();
getText(root, texts);
super.setResult(texts);
return true;
}
private void getText(AccessibilityNodeInfo nodeInfo, List<String> texts) {
CharSequence text = nodeInfo.getText();
if (text != null && text.length() != 0) {
texts.add(text.toString());
}
for (int i = 0; i < nodeInfo.getChildCount(); i++) {
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(nodeInfo, i);
if (child != null) {
getText(child, texts);
child.recycle();
}
}
}
@Override
public void setResult(Object result) {
if (result instanceof List)
super.setResult(result);
}
}

View File

@@ -36,11 +36,11 @@ public class ScrollAction extends SimpleAction {
private List<AccessibilityNodeInfo> findScrollableNodes(AccessibilityNodeInfo root) {
List<AccessibilityNodeInfo> list = new ArrayList<>();
findScrollableNodes(root, list);
findScrollableNodes(getAllocator(), root, list);
return list;
}
private static boolean findScrollableNodes(AccessibilityNodeInfo node, List<AccessibilityNodeInfo> list) {
private static boolean findScrollableNodes(AccessibilityNodeInfoAllocator allocator, AccessibilityNodeInfo node, List<AccessibilityNodeInfo> list) {
if (node == null) {
return false;
}
@@ -48,10 +48,10 @@ public class ScrollAction extends SimpleAction {
list.add(node);
}
for (int i = 0; i < node.getChildCount(); i++) {
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(node, i);
AccessibilityNodeInfo child = allocator.getChild(node, i);
if (child == null)
continue;
if (!findScrollableNodes(child, list))
if (!findScrollableNodes(allocator, child, list))
child.recycle();
}
return node.isScrollable();

View File

@@ -51,7 +51,7 @@ public class ScrollMaxAction extends SimpleAction {
}
}
for (int i = 0; i < nodeInfo.getChildCount(); i++) {
AccessibilityNodeInfo child = AccessibilityNodeInfoAllocator.getGlobal().getChild(nodeInfo, i);
AccessibilityNodeInfo child = getAllocator().getChild(nodeInfo, i);
if (child != null) {
findMaxScrollableNodeInfo(child);
if (mMaxScrollableNode != child) {

View File

@@ -33,7 +33,7 @@ public class SearchUpTargetAction extends SearchTargetAction {
node.recycle();
return null;
}
AccessibilityNodeInfo parent = AccessibilityNodeInfoAllocator.getGlobal().getParent(node);
AccessibilityNodeInfo parent = getAllocator().getParent(node);
list.add(node);
node = parent;
}

View File

@@ -1,31 +1,48 @@
package com.stardust.automator.simple_action;
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.List;
/**
* Created by Stardust on 2017/1/27.
*/
public abstract class SimpleAction {
private boolean mValid = true;
private Object mResult = false;
private volatile AccessibilityNodeInfoAllocator mAllocator = AccessibilityNodeInfoAllocator.NONE;
private volatile boolean mValid = true;
private volatile boolean mResult = false;
public abstract boolean perform(AccessibilityNodeInfo root);
public synchronized Object getResult() {
public boolean getResult() {
return mResult;
}
public synchronized void setResult(Object result) {
public void setResult(boolean result) {
mResult = result;
}
public synchronized void setValid(boolean valid) {
public void setValid(boolean valid) {
mValid = valid;
}
public synchronized boolean isValid() {
public boolean isValid() {
return mValid;
}
public AccessibilityNodeInfoAllocator getAllocator() {
return mAllocator;
}
public void setAllocator(@NonNull AccessibilityNodeInfoAllocator allocator) {
mAllocator = allocator;
}
}

View File

@@ -56,7 +56,6 @@ public class SimpleActionPerformHost implements AccessibilityDelegate {
private synchronized void onActionPerformed(SimpleAction simpleAction) {
AccessibilityNodeInfoAllocator.getGlobal().recycleAll();
synchronized (simpleAction) {
simpleAction.notify();
}

View File

@@ -17,8 +17,8 @@ import com.stardust.view.accessibility.AccessibilityDelegate;
public class AccessibilityInfoProvider implements AccessibilityDelegate {
private String mLatestPackage = "";
private String mLatestActivity = "";
private volatile String mLatestPackage = "";
private volatile String mLatestActivity = "";
private PackageManager mPackageManager;
public AccessibilityInfoProvider(PackageManager packageManager) {

View File

@@ -1,5 +1,7 @@
package com.stardust.view.accessibility;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.util.Log;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -104,9 +106,10 @@ public class AccessibilityNodeInfoAllocator {
return notRecycledCount;
}
private AccessibilityNodeInfo add(AccessibilityNodeInfo nodeInfo) {
private AccessibilityNodeInfo add(@Nullable AccessibilityNodeInfo nodeInfo) {
String stackTrace = DEBUG ? Arrays.toString(Thread.currentThread().getStackTrace()) : null;
mAccessibilityNodeInfoList.put(nodeInfo, stackTrace);
if (nodeInfo != null)
mAccessibilityNodeInfoList.put(nodeInfo, stackTrace);
return nodeInfo;
}
@@ -117,8 +120,6 @@ public class AccessibilityNodeInfoAllocator {
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);
}
}
}

View File

@@ -5,7 +5,7 @@ buildscript {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
classpath 'com.android.tools.build:gradle:2.3.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@@ -2,7 +2,6 @@ package com.stardust.pio;
import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
@@ -11,6 +10,7 @@ import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
/**
* Created by Stardust on 2017/4/1.
@@ -20,18 +20,34 @@ public class PFile {
private static final String TAG = "PFile";
private static final int BUFFER_SIZE = 8192;
static final int DEFAULT_BUFFER_SIZE = 8192;
static final String DEFAULT_ENCODING = Charset.defaultCharset().name();
public static PFile open(String path, String mode) {
public static PFile open(String path, String mode, String encoding, int bufferSize) {
switch (mode) {
case "r":
return new PReadableFile(path);
return new PReadableTextFile(path, encoding, bufferSize);
case "w":
return new PWritableFile();
return new PWritableTextFile(path, encoding, bufferSize, false);
case "a":
return new PWritableTextFile(path, encoding, bufferSize, true);
}
return null;
}
public static PFile open(String path, String mode, String encoding) {
return open(path, mode, encoding, DEFAULT_BUFFER_SIZE);
}
public static PFile open(String path, String mode) {
return open(path, mode, DEFAULT_ENCODING, DEFAULT_BUFFER_SIZE);
}
public static PFile open(String path) {
return open(path, "r", DEFAULT_ENCODING, DEFAULT_BUFFER_SIZE);
}
public static boolean createIfNotExists(String path) {
ensureDirectory(path);
File file = new File(path);
@@ -119,7 +135,7 @@ public class PFile {
}
public static boolean write(InputStream is, OutputStream os) {
byte[] buffer = new byte[BUFFER_SIZE];
byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
try {
while (is.available() > 0) {
int n = is.read(buffer);
@@ -155,16 +171,16 @@ public class PFile {
public static String renameWithoutExtension(String path, String newName) {
File file = new File(path);
File newFile = new File(file.getParent(), newName + "." + getExtension(file.getName()));
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)
if (i < 0 || i == fileName.length() - 1)
return "";
return fileName.substring(i + 1);
return fileName.substring(i);
}
public static boolean write(String path, String text) {
@@ -223,7 +239,7 @@ public class PFile {
name += name.hashCode();
}
try {
File tmpFile = File.createTempFile(name, "." + extension, context.getCacheDir());
File tmpFile = File.createTempFile(name, extension, context.getCacheDir());
copyAsset(context, path, tmpFile.getPath());
return tmpFile;
} catch (IOException e) {

View File

@@ -0,0 +1,10 @@
package com.stardust.pio;
/**
* Created by Stardust on 2017/4/6.
*/
public class PReadableBinaryFile extends PFile {
}

View File

@@ -1,7 +1,6 @@
package com.stardust.pio;
import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@@ -9,22 +8,22 @@ import java.util.List;
* Created by Stardust on 2017/4/1.
*/
public class PReadableFile extends PFile {
public class PReadableTextFile extends PFile {
private BufferedReader mBufferedReader;
private FileInputStream mFileInputStream;
private int mBufferingSize;
private String mEncoding;
public PReadableFile(String path) {
this(path, Charset.defaultCharset().name());
public PReadableTextFile(String path) {
this(path, PFile.DEFAULT_ENCODING);
}
public PReadableFile(String path, String encoding) {
public PReadableTextFile(String path, String encoding) {
this(path, encoding, -1);
}
public PReadableFile(String path, String encoding, int bufferingSize) {
public PReadableTextFile(String path, String encoding, int bufferingSize) {
mEncoding = encoding;
mBufferingSize = bufferingSize;
try {

View File

@@ -1,15 +0,0 @@
package com.stardust.pio;
/**
* Created by Stardust on 2017/4/1.
*/
public class PWritableFile extends PFile {
public PWritableFile(){
}
}

View File

@@ -0,0 +1,116 @@
package com.stardust.pio;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
/**
* Created by Stardust on 2017/4/1.
*/
public class PWritableTextFile extends PFile {
public static PWritableTextFile open(String path, String encoding, int bufferSize) {
return new PWritableTextFile(path, encoding, bufferSize, false);
}
public static PWritableTextFile open(String path, String encoding) {
return new PWritableTextFile(path, encoding);
}
public static PWritableTextFile open(String path, boolean append) {
return new PWritableTextFile(path, append);
}
public static PWritableTextFile open(String path) {
return new PWritableTextFile(path);
}
private BufferedWriter mBufferedWriter;
public PWritableTextFile(String path, String encoding, int bufferingSize, boolean append) {
if (bufferingSize <= 0) {
bufferingSize = PFile.DEFAULT_BUFFER_SIZE;
}
try {
mBufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(path, append), encoding), bufferingSize);
} catch (UnsupportedEncodingException | FileNotFoundException e) {
throw new UncheckedIOException(e);
}
}
public PWritableTextFile(String path) {
this(path, PFile.DEFAULT_ENCODING, PFile.DEFAULT_BUFFER_SIZE, false);
}
public PWritableTextFile(String path, boolean append) {
this(path, PFile.DEFAULT_ENCODING, PFile.DEFAULT_BUFFER_SIZE, append);
}
public PWritableTextFile(String path, int bufferSize) {
this(path, PFile.DEFAULT_ENCODING, bufferSize, false);
}
public PWritableTextFile(String path, String encoding) {
this(path, encoding, DEFAULT_BUFFER_SIZE, false);
}
public PWritableTextFile(String path, String encoding, boolean append) {
this(path, encoding, DEFAULT_BUFFER_SIZE, append);
}
public PWritableTextFile(String path, String encoding, int bufferSize) {
this(path, encoding, bufferSize, false);
}
public void write(String str) {
try {
mBufferedWriter.write(str);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void writeline(String line) {
try {
mBufferedWriter.write(line);
mBufferedWriter.newLine();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void writelines(String[] lines) {
writelines(Arrays.asList(lines));
}
public void writelines(List<String> lines) {
for (String line : lines) {
writeline(line);
}
}
public void close() {
try {
mBufferedWriter.close();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public void flush() {
try {
mBufferedWriter.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}

View File

@@ -11,4 +11,9 @@ public class UncheckedIOException extends RuntimeException {
public UncheckedIOException(IOException cause) {
super(cause);
}
@Override
public synchronized IOException getCause() {
return (IOException) super.getCause();
}
}

View File

@@ -0,0 +1,22 @@
package com.stardust.util;
import android.support.annotation.Nullable;
/**
* Created by Stardust on 2017/4/5.
*/
public class DeveloperUtils {
private static final String PACKAGE_NAME = "com.stardust.scriptdroid";
public static void ensureRunningPackageNotSelf(@Nullable String runningPackage) {
if (PACKAGE_NAME.equals(runningPackage)) {
throw new SecurityException();
}
}
public static boolean isRunningPackageSelf(@Nullable String runningPackage) {
return PACKAGE_NAME.equals(runningPackage);
}
}

26
version.json Normal file
View File

@@ -0,0 +1,26 @@
{
"versionCode": 117,
"versionName": "2.0.7 Beta",
"releaseNotes": "* 新增 任务管理\n* 新增 代码格式化\n* 新增 免root模拟home键, 电源键, 多任务键等\n* 新增 真正支持Takser调用(插件)\n* 新增 新建文件夹\n* 新增 支持代码模块化(CommonJS)\n* 新增 支持NodeJs的部分模块",
"downloads" : [
{
name: "酷安",
url: "http://www.coolapk.com/apk/com.stardust.scriptdroid"
},
{
name: "应用宝",
url: "http://a.app.qq.com/o/simple.jsp?pkgname=com.stardust.scriptdroid"
}
],
"oldVersions": [
{
"versionCode": 115,
"issues": "[测试] 当前版本为Beta版本,问题很多,请尽快更新正式版"
},
{
"versionCode": "116",
"issues": "[测试] 当前版本为Beta版本,请尽快更新正式版"
}
],
"deprecated": 116
}