Attempt to fix selector memeory leak. Add update checker

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

View File

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

View File

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

View File

@@ -63,16 +63,6 @@ public class AutoJs implements AccessibilityBridge {
.build();
ScriptEngineService.setInstance(mScriptEngineService);
addAccessibilityServiceDelegates();
getGlobalFunctions();
}
private void getGlobalFunctions() {
new Thread(new Runnable() {
@Override
public void run() {
mScriptEngineService.getGlobalFunctions();
}
}).start();
}
private void addAccessibilityServiceDelegates() {
@@ -114,8 +104,8 @@ public class AutoJs implements AccessibilityBridge {
if (AccessibilityServiceUtils.isAccessibilityServiceEnabled(App.getApp(), AccessibilityWatchDogService.class)) {
errorMessage = App.getApp().getString(R.string.text_auto_operate_service_enabled_but_not_running);
} else {
if (Pref.def().getBoolean(App.getApp().getString(R.string.key_enable_accessibility_service_by_root), false)) {
if (!AccessibilityServiceTool.enableAccessibilityServiceByRootAndWaitFor(AccessibilityWatchDogService.class, 3000)) {
if (Pref.enableAccessibilityServiceByRoot()) {
if (!AccessibilityServiceTool.enableAccessibilityServiceByRootAndWaitFor(2000)) {
errorMessage = App.getApp().getString(R.string.text_enable_accessibility_service_by_root_timeout);
}
} else {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -84,8 +84,6 @@ public class EditActivity extends Editor920Activity {
public static final String EXTRA_CONTENT = "Still Love Eating 17.4.5";
private static final String KEY_EDIT_ACTIVITY_FIRST_USE = "KEY_EDIT_ACTIVITY_FIRST_USE";
private static final String ACTION_ON_RUN_FINISHED = "ACTION_ON_RUN_FINISHED";
private static final String EXTRA_EXCEPTION_MESSAGE = "EXTRA_EXCEPTION_MESSAGE";
@@ -173,14 +171,13 @@ public class EditActivity extends Editor920Activity {
}
private void openDrawerIfFirstUse() {
if (Pref.def().getBoolean(KEY_EDIT_ACTIVITY_FIRST_USE, true)) {
if (Pref.isEditActivityFirstUsing()) {
mDrawerLayout.postDelayed(new Runnable() {
@Override
public void run() {
mDrawerLayout.openDrawer(GravityCompat.END);
}
}, 1000);
Pref.def().edit().putBoolean(KEY_EDIT_ACTIVITY_FIRST_USE, false).apply();
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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