Attempt to fix selector memeory leak. Add update checker
This commit is contained in:
@@ -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')
|
||||
|
||||
58
app/proguard-rules.pro
vendored
58
app/proguard-rules.pro
vendored
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
BIN
app/src/main/res/drawable/ic_folder_outline_white.png
Normal file
BIN
app/src/main/res/drawable/ic_folder_outline_white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -56,7 +56,6 @@ public class SimpleActionPerformHost implements AccessibilityDelegate {
|
||||
|
||||
|
||||
private synchronized void onActionPerformed(SimpleAction simpleAction) {
|
||||
AccessibilityNodeInfoAllocator.getGlobal().recycleAll();
|
||||
synchronized (simpleAction) {
|
||||
simpleAction.notify();
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.stardust.pio;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/6.
|
||||
*/
|
||||
|
||||
public class PReadableBinaryFile extends PFile {
|
||||
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
@@ -1,15 +0,0 @@
|
||||
package com.stardust.pio;
|
||||
|
||||
/**
|
||||
* Created by Stardust on 2017/4/1.
|
||||
*/
|
||||
|
||||
public class PWritableFile extends PFile {
|
||||
|
||||
public PWritableFile(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
116
common/src/main/java/com/stardust/pio/PWritableTextFile.java
Normal file
116
common/src/main/java/com/stardust/pio/PWritableTextFile.java
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -11,4 +11,9 @@ public class UncheckedIOException extends RuntimeException {
|
||||
public UncheckedIOException(IOException cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized IOException getCause() {
|
||||
return (IOException) super.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
22
common/src/main/java/com/stardust/util/DeveloperUtils.java
Normal file
22
common/src/main/java/com/stardust/util/DeveloperUtils.java
Normal 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
26
version.json
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user