Add: floating window, layout inspector

This commit is contained in:
hyb1996
2017-03-12 23:10:58 +08:00
parent a211d208d0
commit e81b661974
164 changed files with 7045 additions and 1206 deletions

View File

@@ -2,20 +2,28 @@ package com.stardust.scriptdroid;
import android.app.Activity;
import android.app.Application;
import android.graphics.Paint;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.TextPaint;
import android.util.Log;
import com.squareup.leakcanary.LeakCanary;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistant;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformAccessibilityDelegate;
import com.stardust.automator.AccessibilityEventCommandHost;
import com.stardust.scriptdroid.accessibility.AccessibilityInfoProvider;
import com.stardust.scriptdroid.layout_inspector.LayoutInspector;
import com.stardust.scriptdroid.record.AccessibilityRecorderDelegate;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.squareup.leakcanary.LeakCanary;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformAccessibilityDelegate;
import com.stardust.scriptdroid.tool.ViewTool;
import com.stardust.scriptdroid.ui.error.ErrorReportActivity;
import com.stardust.theme.ThemeColorManager;
import com.stardust.util.CrashHandler;
import com.stardust.util.StateObserver;
import java.lang.ref.WeakReference;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* Created by Stardust on 2017/1/27.
@@ -23,6 +31,8 @@ import java.lang.ref.WeakReference;
public class App extends Application {
private static final String TAG = "App";
private static WeakReference<App> instance;
private static StateObserver stateObserver;
private static WeakReference<Activity> currentActivity;
@@ -49,14 +59,15 @@ public class App extends Application {
stateObserver = new StateObserver(PreferenceManager.getDefaultSharedPreferences(this));
registerActivityLifecycleCallback();
initAccessibilityServiceDelegates();
}
private void initAccessibilityServiceDelegates() {
AccessibilityWatchDogService.addDelegateIfNeeded(100, ActionPerformAccessibilityDelegate.class);
AccessibilityWatchDogService.addDelegateIfNeeded(200, AccessibilityRecorderDelegate.getInstance());
AccessibilityWatchDogService.addDelegateIfNeeded(300, BoundsAssistant.class);
AccessibilityWatchDogService.addDelegateIfNeeded(300, AccessibilityEventCommandHost.instance);
AccessibilityWatchDogService.addDelegateIfNeeded(400, AccessibilityInfoProvider.instance);
AccessibilityWatchDogService.addDelegateIfNeeded(500, LayoutInspector.getInstance());
}
@@ -85,6 +96,10 @@ public class App extends Application {
return currentActivity.get();
}
public static String getResString(int id) {
return getApp().getString(id);
}
private static class SimpleActivityLifecycleCallbacks implements ActivityLifecycleCallbacks {

View File

@@ -0,0 +1,51 @@
package com.stardust.scriptdroid.accessibility;
import android.accessibilityservice.AccessibilityService;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.view.accessibility.AccessibilityEvent;
import com.stardust.scriptdroid.App;
import com.stardust.view.accessibility.AccessibilityDelegate;
/**
* Created by Stardust on 2017/3/9.
*/
public class AccessibilityInfoProvider implements AccessibilityDelegate {
public static AccessibilityInfoProvider instance = new AccessibilityInfoProvider();
private String mLatestPackage = "";
private String mLatestActivity = "";
public synchronized String getLatestPackage() {
return mLatestPackage;
}
public synchronized String getLatestActivity() {
return mLatestActivity;
}
@Override
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
setLatestComponent(event.getPackageName(), event.getClassName());
return false;
}
private synchronized void setLatestComponent(CharSequence latestPackage, CharSequence latestClass) {
if (latestPackage == null)
return;
mLatestPackage = latestPackage.toString();
if (latestClass == null)
return;
try {
ComponentName componentName = new ComponentName(latestPackage.toString(), latestClass.toString());
ActivityInfo activityInfo = App.getApp().getPackageManager().getActivityInfo(componentName, 0);
mLatestActivity = activityInfo.name;
} catch (PackageManager.NameNotFoundException ignored) {
}
}
}

View File

@@ -1,25 +0,0 @@
package com.stardust.scriptdroid.bounds_assist;
/**
* Created by Stardust on 2017/2/4.
*/
public interface BoundsAssistClipList {
interface OnClipChangedListener {
void onClipRemove(int position);
void onClipInsert(int position);
void onChange();
}
int size();
String get(int i);
void add(String clip);
void setOnClipChangedListener(OnClipChangedListener listener);
}

View File

@@ -1,93 +0,0 @@
package com.stardust.scriptdroid.bounds_assist;
import android.accessibilityservice.AccessibilityService;
import android.graphics.Rect;
import android.preference.PreferenceManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.service.AccessibilityDelegate;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
/**
* Created by Stardust on 2017/2/4.
*/
public class BoundsAssistant implements AccessibilityDelegate {
public static final String KEY_BOUNDS_ASSIST_ENABLE = "ASSIST_MODE_ENABLE";
private static boolean assistModeEnable;
private static BoundsAssistClipList boundsAssistClipList = SharedPrefBoundsAssistClipList.getInstance();
public static boolean isAssistModeEnable() {
return assistModeEnable;
}
public static void setAssistModeEnable(boolean assistModeEnable) {
if (assistModeEnable && Pref.isFirstEnableAssistMode()) {
showAssistModeInfoDialog();
}
BoundsAssistant.assistModeEnable = assistModeEnable;
App.getStateObserver().setState(KEY_BOUNDS_ASSIST_ENABLE, assistModeEnable);
}
private static void showAssistModeInfoDialog() {
new ThemeColorMaterialDialogBuilder(App.currentActivity())
.title(R.string.text_alert)
.content(R.string.assist_mode_notice)
.positiveText(R.string.ok)
.show();
}
private static void performAssistance(AccessibilityEvent event) {
if (!assistModeEnable) {
return;
}
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED || event.getEventType() == AccessibilityEvent.TYPE_VIEW_LONG_CLICKED) {
AccessibilityNodeInfo nodeInfo = event.getSource();
if (nodeInfo == null) {
return;
}
nodeInfo.refresh();
Rect rect = getBoundsInScreen(nodeInfo);
saveAndAlertBounds(rect);
nodeInfo.recycle();
}
}
@Override
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
performAssistance(event);
return false;
}
public static Rect getBoundsInScreen(AccessibilityNodeInfo nodeInfo) {
Rect rect = new Rect();
nodeInfo.getBoundsInScreen(rect);
return rect;
}
private static void saveAndAlertBounds(Rect rect) {
String str = boundsToString(rect);
boundsAssistClipList.add(str);
Toast.makeText(App.getApp(), str, Toast.LENGTH_SHORT).show();
}
public static String boundsToString(Rect rect) {
return rect.toString().replace('-', ',').replace(" ", "").substring(4);
}
static {
assistModeEnable = PreferenceManager.getDefaultSharedPreferences(App.getApp()).getBoolean(KEY_BOUNDS_ASSIST_ENABLE, false);
}
}

View File

@@ -1,89 +0,0 @@
package com.stardust.scriptdroid.bounds_assist;
import android.content.Context;
import android.content.SharedPreferences;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.stardust.scriptdroid.App;
import java.lang.reflect.Type;
import java.util.LinkedList;
import java.util.List;
/**
* Created by Stardust on 2017/2/4.
*/
public class SharedPrefBoundsAssistClipList implements BoundsAssistClipList {
private static final Gson GSON = new Gson();
private static SharedPrefBoundsAssistClipList instance = new SharedPrefBoundsAssistClipList(App.getApp());
private OnClipChangedListener mOnClipChangedListener;
public static SharedPrefBoundsAssistClipList getInstance() {
return instance;
}
private static final int MAX_SIZE = 10;
private static final String SHARED_PREF_NAME = "SharedPrefBoundsAssistClipList";
private SharedPreferences mSharedPreferences;
private List<String> mAssistClipList = new LinkedList<>();
public SharedPrefBoundsAssistClipList(Context context) {
mSharedPreferences = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE);
readFromSharedPref();
}
private void readFromSharedPref() {
Type type = new TypeToken<List<String>>() {
}.getType();
List<String> l = GSON.fromJson(mSharedPreferences.getString(SHARED_PREF_NAME, ""), type);
if (l != null)
mAssistClipList.addAll(l);
}
@Override
public synchronized int size() {
return mAssistClipList.size();
}
@Override
public synchronized String get(int i) {
return mAssistClipList.get(i);
}
@Override
public synchronized void add(String clip) {
mAssistClipList.add(0, clip);
notifyInsert();
if (mAssistClipList.size() > MAX_SIZE) {
mAssistClipList.remove(mAssistClipList.size() - 1);
notifyRemove();
}
syncWithSharedPref();
}
private void notifyRemove() {
if (mOnClipChangedListener != null)
mOnClipChangedListener.onClipRemove(mAssistClipList.size());
}
private void notifyInsert() {
if (mOnClipChangedListener != null) {
mOnClipChangedListener.onClipInsert(0);
}
}
@Override
public void setOnClipChangedListener(OnClipChangedListener listener) {
mOnClipChangedListener = listener;
}
private void syncWithSharedPref() {
mSharedPreferences.edit().putString(SHARED_PREF_NAME, GSON.toJson(mAssistClipList)).apply();
}
}

View File

@@ -16,21 +16,21 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.automator.AccessibilityEventCommandHost;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.droid.runtime.action.ActionTarget;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.jraska.console.Console;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.runtime.action.Action;
import com.stardust.scriptdroid.droid.runtime.action.ActionFactory;
import com.stardust.scriptdroid.droid.runtime.action.ActionPerformAccessibilityDelegate;
import com.stardust.scriptdroid.droid.runtime.action.ActionTarget;
import com.stardust.scriptdroid.droid.runtime.action.GetTextAction;
import com.stardust.scriptdroid.file.FileUtils;
import com.stardust.scriptdroid.service.AccessibilityDelegate;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.tool.Shell;
import com.stardust.scriptdroid.ui.console.ConsoleActivity;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import java.io.File;
@@ -194,7 +194,7 @@ public class DroidRuntime {
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 (!AccessibilityServiceUtils.enableAccessibilityServiceByRootAndWaitFor(App.getApp(), AccessibilityWatchDogService.class, 3000)) {
if (!AccessibilityServiceTool.enableAccessibilityServiceByRootAndWaitFor(AccessibilityWatchDogService.class, 3000)) {
errorMessage = App.getApp().getString(R.string.text_enable_accessibility_service_by_root_timeout);
}
} else {
@@ -208,10 +208,6 @@ public class DroidRuntime {
}
}
public List<String> getTexts() {
return performAction(new GetTextAction());
}
public void toast(final String text) {
mUIHandler.post(new Runnable() {
@Override
@@ -229,16 +225,10 @@ public class DroidRuntime {
}
}
public void addAccessibilityDelegate(final Object delegate) {
public void addAccessibilityEventListener(final Object delegate) {
if (delegate == null)
return;
AccessibilityWatchDogService.addDelegate(new AccessibilityDelegate() {
@Override
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
return false;
}
}, 500);
}
public Shell.CommandResult shell(String cmd, int root) {
@@ -255,6 +245,10 @@ public class DroidRuntime {
return ActionPerformAccessibilityDelegate.getLatestActivity();
}
public UiSelector selector() {
return new UiSelector();
}
public boolean isStopped() {
return Thread.currentThread().isInterrupted();
}

View File

@@ -0,0 +1,228 @@
package com.stardust.scriptdroid.droid.runtime;
import android.accessibilityservice.AccessibilityService;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.bezyapps.floatieslibrary.Floaty;
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.scriptdroid.accessibility.AccessibilityInfoProvider;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_COLUMN_INT;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_PROGRESS_VALUE;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_ROW_INT;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_CLEAR_FOCUS;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_CLICK;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_COLLAPSE;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_COPY;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_CUT;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_DISMISS;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_EXPAND;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_FOCUS;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_LONG_CLICK;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_PASTE;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_SELECT;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_SET_SELECTION;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.ACTION_SET_TEXT;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_CONTEXT_CLICK;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_DOWN;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_LEFT;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_RIGHT;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_TO_POSITION;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SCROLL_UP;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SET_PROGRESS;
import static android.support.v4.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_SHOW_ON_SCREEN;
/**
* Created by Stardust on 2017/3/9.
*/
public class UiSelector extends UiGlobalSelector {
private class FindCommand implements AccessibilityEventCommandHost.Command {
UiObjectCollection result;
@Override
public void execute(AccessibilityService service, AccessibilityEvent event) {
AccessibilityNodeInfo root = service.getRootInActiveWindow();
if (root != null) {
result = findOf(root);
}
}
}
private static final String TAG = "UiSelector";
public UiObjectCollection find() {
FindCommand command = new FindCommand();
AccessibilityEventCommandHost.instance.executeAndWaitForEvent(command);
return command.result;
}
@NonNull
public UiObjectCollection untilFind() {
UiObjectCollection uiObjectCollection;
do {
uiObjectCollection = find();
} while (uiObjectCollection == null || uiObjectCollection.size() == 0);
return uiObjectCollection;
}
public UiObject findOne() {
UiObjectCollection collection = find();
if (collection == null || collection.size() == 0) {
return null;
}
return new UiObject(collection.get(0).getInfo());
}
@NonNull
public UiObject untilFindOne() {
UiObjectCollection collection = untilFind();
return new UiObject(collection.get(0).getInfo());
}
public UiSelector id(final String id) {
if (!id.contains(":")) {
addFilter(new DfsFilter() {
@Override
protected boolean isIncluded(AccessibilityNodeInfo nodeInfo) {
String fullId = AccessibilityInfoProvider.instance.getLatestPackage() + ":id/" + id;
return fullId.equals(nodeInfo.getViewIdResourceName());
}
});
} else {
super.id(id);
}
return this;
}
public boolean performAction(int action, ActionArgument... arguments) {
return untilFind().performAction(action, arguments);
}
public boolean click() {
return performAction(ACTION_CLICK);
}
public boolean longClick() {
return performAction(ACTION_LONG_CLICK);
}
public boolean accessibilityFocus() {
return performAction(ACTION_ACCESSIBILITY_FOCUS);
}
public boolean clearAccessibilityFocus() {
return performAction(ACTION_CLEAR_ACCESSIBILITY_FOCUS);
}
public boolean focus() {
return performAction(ACTION_FOCUS);
}
public boolean clearFocus() {
return performAction(ACTION_CLEAR_FOCUS);
}
public boolean copy() {
return performAction(ACTION_COPY);
}
public boolean paste() {
return performAction(ACTION_PASTE);
}
public boolean select() {
return performAction(ACTION_SELECT);
}
public boolean cut() {
return performAction(ACTION_CUT);
}
public boolean collapse() {
return performAction(ACTION_COLLAPSE);
}
public boolean expand() {
return performAction(ACTION_EXPAND);
}
public boolean dismiss() {
return performAction(ACTION_DISMISS);
}
public boolean show() {
return performAction(ACTION_SHOW_ON_SCREEN.getId());
}
public boolean scrollForward() {
return performAction(ACTION_SCROLL_FORWARD);
}
public boolean scrollBackward() {
return performAction(ACTION_SCROLL_BACKWARD);
}
public boolean scrollUp() {
return performAction(ACTION_SCROLL_UP.getId());
}
public boolean scrollDown() {
return performAction(ACTION_SCROLL_DOWN.getId());
}
public boolean scrollLeft() {
return performAction(ACTION_SCROLL_LEFT.getId());
}
public boolean scrollRight() {
return performAction(ACTION_SCROLL_RIGHT.getId());
}
public boolean contextClick() {
return performAction(ACTION_CONTEXT_CLICK.getId());
}
public boolean setSelection(int s, int e) {
return performAction(ACTION_SET_SELECTION,
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_SELECTION_START_INT, s),
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_SELECTION_END_INT, e));
}
public boolean setText(String text) {
return performAction(ACTION_SET_TEXT,
new ActionArgument.CharSequenceActionArgument(ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text));
}
public boolean setProgress(float value) {
return performAction(ACTION_SET_PROGRESS.getId(),
new ActionArgument.FloatActionArgument(ACTION_ARGUMENT_PROGRESS_VALUE, value));
}
public boolean scrollTo(int row, int column) {
return performAction(ACTION_SCROLL_TO_POSITION.getId(),
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_ROW_INT, row),
new ActionArgument.IntActionArgument(ACTION_ARGUMENT_COLUMN_INT, column));
}
}

View File

@@ -9,7 +9,6 @@ import com.stardust.util.SparseArrayEntries;
* Created by Stardust on 2017/1/27.
*/
@FunctionalInterface
public interface Able {
SparseArray<Able> ABLE_MAP = new SparseArrayEntries<Able>()

View File

@@ -10,7 +10,7 @@ import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
import com.stardust.scriptdroid.service.AccessibilityDelegate;
import com.stardust.view.accessibility.AccessibilityDelegate;
/**
@@ -50,6 +50,7 @@ public class ActionPerformAccessibilityDelegate implements AccessibilityDelegate
AccessibilityNodeInfo root = service.getRootInActiveWindow();
if (root == null) {
Log.v(TAG, "root = null");
return false;
}
Log.i(TAG, "perform action:" + action);
if (action.perform(root)) {

View File

@@ -33,8 +33,7 @@ public abstract class JavaScriptEngine {
try {
return FileUtils.readString(App.getApp().getAssets().open("javasccript_engine_init.js"));
} catch (IOException e) {
e.printStackTrace();
return "Error: Unable to read init script.";
throw new RuntimeException(e);
}
}

View File

@@ -1,7 +1,7 @@
package com.stardust.scriptdroid.droid.script;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
import com.stardust.scriptdroid.droid.runtime.ScriptStopException;
@@ -58,10 +58,10 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
private void init(Context context, Scriptable scope) {
ScriptableObject.putProperty(scope, "context", App.getApp());
ScriptableObject.putProperty(scope, "__engine__", "rhino");
context.evaluateString(scope, Init.getInitScript(), "<init>", 1, null);
for (Map.Entry<String, Object> variable : mVariableMap.entrySet()) {
ScriptableObject.putProperty(scope, variable.getKey(), variable.getValue());
}
context.evaluateString(scope, Init.getInitScript(), "<init>", 1, null);
mThreads.add(Thread.currentThread());
}

View File

@@ -8,8 +8,6 @@ import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.droid.RunningConfig;
import static com.stardust.scriptdroid.droid.Droid.JAVA_SCRIPT_ENGINE;
/**
* Created by Stardust on 2017/2/5.
*/
@@ -43,9 +41,9 @@ public class ScriptExecuteActivity extends Activity {
}
private void runScript() {
JAVA_SCRIPT_ENGINE.set("activity", this);
Droid.JAVA_SCRIPT_ENGINE.set("activity", this);
try {
mResult = JAVA_SCRIPT_ENGINE.execute(mScript);
mResult = Droid.JAVA_SCRIPT_ENGINE.execute(mScript);
} catch (Exception e) {
mOnRunFinishedListener.onException(e);
finish();
@@ -61,7 +59,7 @@ public class ScriptExecuteActivity extends Activity {
@Override
protected void onDestroy() {
super.onDestroy();
JAVA_SCRIPT_ENGINE.set("activity", null);
JAVA_SCRIPT_ENGINE.removeAndDestroy();
Droid.JAVA_SCRIPT_ENGINE.set("activity", null);
Droid.JAVA_SCRIPT_ENGINE.removeAndDestroy();
}
}

View File

@@ -2,9 +2,9 @@ package com.stardust.scriptdroid.droid.script.file;
import android.os.Environment;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.Droid;
import java.io.File;

View File

@@ -0,0 +1,32 @@
package com.stardust.scriptdroid.external.floating_window;
import android.content.Intent;
import android.view.View;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.layout_inspector.view.LayoutInspectView;
import com.stardust.view.Floaty;
import com.stardust.view.ResizableFloaty;
/**
* Created by Stardust on 2017/3/10.
*/
public class FloatingWindowManger {
public static void showFloatingWindow() {
if (!HoverMenuService.isServiceRunning())
App.getApp().startService(new Intent(App.getApp(), HoverMenuService.class));
}
public static boolean isFloatingWindowShowing() {
return HoverMenuService.isServiceRunning();
}
public static void hideFloatingWindow() {
if (HoverMenuService.isServiceRunning())
HoverMenuService.stopService();
}
}

View File

@@ -0,0 +1,214 @@
package com.stardust.scriptdroid.external.floating_window;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.TypedValue;
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.theme.ThemeColorManagerCompat;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import io.mattcarroll.hover.NavigatorContent;
/**
* Created by Stardust on 2017/3/11.
*/
public class HoverMenuAdapter implements io.mattcarroll.hover.HoverMenuAdapter {
public static final String ID_MAIN = "intro";
public static final String ID_RECORD = "record";
public static final String ID_SCRIPT_LIST = "script_list";
public static final String MENU_ID = "menu";
public static final String PLACEHOLDER_ID = "placeholder";
private final Context mContext;
private final List<String> mTabIds;
private final Map<String, NavigatorContent> mData = new LinkedHashMap<>();
private final Set<ContentChangeListener> mContentChangeListeners = new HashSet<>();
private View[] mViews;
public HoverMenuAdapter(@NonNull Context context) {
mContext = context;
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));
mTabIds = new ArrayList<>();
for (String tabId : mData.keySet()) {
mTabIds.add(tabId);
}
mViews = new View[mTabIds.size()];
}
@Override
public int getTabCount() {
return mTabIds.size();
}
@Override
public View getTabView(int index) {
mViews[index] = getTabViewInner(index);
return mViews[index];
}
private View getTabViewInner(int index) {
String menuItemId = mTabIds.get(index);
if (ID_MAIN.equals(menuItemId)) {
return createTabView(R.drawable.ic_android_eat_js);
} else if (ID_RECORD.equals(menuItemId)) {
return createTabView(R.drawable.ic_video_record);
} else if (ID_SCRIPT_LIST.equals(menuItemId)) {
return createTabView(R.drawable.ic_menu);
} else {
throw new RuntimeException("Unknown tab selected: " + index);
}
}
@Override
public long getTabId(int position) {
return position;
}
@Override
public NavigatorContent getNavigatorContent(int index) {
String tabId = mTabIds.get(index);
return mData.get(tabId);
}
@Override
public void addContentChangeListener(@NonNull ContentChangeListener listener) {
mContentChangeListeners.add(listener);
}
@Override
public void removeContentChangeListener(@NonNull ContentChangeListener listener) {
mContentChangeListeners.remove(listener);
}
public void selectTab(String id) {
int i = mTabIds.indexOf(id);
mViews[i].performClick();
}
protected void notifyDataSetChanged() {
for (ContentChangeListener listener : mContentChangeListeners) {
listener.onContentChange(this);
}
}
private View createTabView(@DrawableRes int tabBitmapRes) {
return createTabView(tabBitmapRes, ThemeColorManagerCompat.getColorPrimary(), Color.WHITE);
}
private View createTabView(@DrawableRes int tabBitmapRes, @ColorInt int backgroundColor, @ColorInt Integer iconColor) {
Resources resources = mContext.getResources();
int padding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8, resources.getDisplayMetrics());
DemoTabView view = new DemoTabView(mContext, resources.getDrawable(R.drawable.tab_background), resources.getDrawable(tabBitmapRes));
view.setTabBackgroundColor(backgroundColor);
view.setTabForegroundColor(iconColor);
view.setPadding(padding, padding, padding, padding);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
view.setElevation(padding);
}
return view;
}
public class DemoTabView extends View {
private int mBackgroundColor;
private Integer mForegroundColor;
private Drawable mCircleDrawable;
private Drawable mIconDrawable;
private int mIconInsetLeft, mIconInsetTop, mIconInsetRight, mIconInsetBottom;
public DemoTabView(Context context, Drawable backgroundDrawable, Drawable iconDrawable) {
super(context);
mCircleDrawable = backgroundDrawable;
mIconDrawable = iconDrawable;
init();
}
private void init() {
int insetsDp = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getContext().getResources().getDisplayMetrics());
mIconInsetLeft = mIconInsetTop = mIconInsetRight = mIconInsetBottom = insetsDp;
}
public void setTabBackgroundColor(@ColorInt int backgroundColor) {
mBackgroundColor = backgroundColor;
mCircleDrawable.setColorFilter(mBackgroundColor, PorterDuff.Mode.SRC_ATOP);
}
public void setTabForegroundColor(@ColorInt Integer foregroundColor) {
mForegroundColor = foregroundColor;
if (null != mForegroundColor) {
mIconDrawable.setColorFilter(mForegroundColor, PorterDuff.Mode.SRC_ATOP);
} else {
mIconDrawable.setColorFilter(null);
}
}
public void setIcon(@Nullable Drawable icon) {
mIconDrawable = icon;
if (null != mForegroundColor && null != mIconDrawable) {
mIconDrawable.setColorFilter(mForegroundColor, PorterDuff.Mode.SRC_ATOP);
}
updateIconBounds();
invalidate();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// Make circle as large as View minus padding.
mCircleDrawable.setBounds(getPaddingLeft(), getPaddingTop(), w - getPaddingRight(), h - getPaddingBottom());
// Re-size the icon as necessary.
updateIconBounds();
invalidate();
}
private void updateIconBounds() {
if (null != mIconDrawable) {
Rect bounds = new Rect(mCircleDrawable.getBounds());
bounds.set(bounds.left + mIconInsetLeft, bounds.top + mIconInsetTop, bounds.right - mIconInsetRight, bounds.bottom - mIconInsetBottom);
mIconDrawable.setBounds(bounds);
}
}
@Override
protected void onDraw(Canvas canvas) {
mCircleDrawable.draw(canvas);
if (null != mIconDrawable) {
mIconDrawable.draw(canvas);
}
}
}
}

View File

@@ -0,0 +1,191 @@
package com.stardust.scriptdroid.external.floating_window;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import com.stardust.hover.HoverMenuBuilder;
import com.stardust.hover.SimpleHoverMenuTransitionListener;
import com.stardust.hover.WindowHoverMenu;
import com.stardust.scriptdroid.external.floating_window.view.FloatingLayoutBoundsView;
import com.stardust.scriptdroid.external.floating_window.view.FloatingLayoutHierarchyView;
import com.stardust.scriptdroid.layout_inspector.LayoutInspector;
import com.stardust.scriptdroid.layout_inspector.NodeInfo;
import com.stardust.theme.ThemeColorManagerCompat;
import com.stardust.util.MessageEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import java.lang.ref.WeakReference;
import io.mattcarroll.hover.HoverMenu;
import io.mattcarroll.hover.defaulthovermenu.window.WindowViewController;
/**
* Created by Stardust on 2017/3/11.
*/
public class HoverMenuService extends Service {
public static final String MESSAGE_SHOW_AND_EXPAND_MENU = "MESSAGE_SHOW_AND_EXPAND_MENU";
public static final String MESSAGE_SHOW_LAYOUT_HIERARCHY = "MESSAGE_SHOW_LAYOUT_HIERARCHY";
public static final String MESSAGE_SHOW_LAYOUT_BOUNDS = "MESSAGE_SHOW_LAYOUT_BOUNDS";
public static final String MESSAGE_COLLAPSE_MENU = "MESSAGE_COLLAPSE_MENU";
public static final String MESSAGE_MENU_COLLAPSING = "MESSAGE_MENU_COLLAPSING";
private static WeakReference<HoverMenuService> service;
public static boolean isServiceRunning() {
return service != null && service.get() != null;
}
public static void stopService() {
if (isServiceRunning()) {
service.get().stopSelf();
}
}
private static final String TAG = "HoverMenuService";
private static final String PREF_FILE = "hover_menu";
private static final String PREF_HOVER_MENU_VISUAL_STATE = "hover_menu_visual_state";
private boolean mIsRunning;
private SharedPreferences mPrefs;
private WindowViewController mWindowViewController;
private FloatingLayoutHierarchyView mFloatingLayoutHierarchyView;
private FloatingLayoutBoundsView mFloatingLayoutBoundsView;
private WindowHoverMenu mWindowHoverMenu;
private HoverMenu.OnExitListener mWindowHoverMenuMenuExitListener = new HoverMenu.OnExitListener() {
@Override
public void onExitByUserRequest() {
savePreferredLocation();
mWindowHoverMenu.hide();
stopSelf();
}
};
@Override
public void onCreate() {
super.onCreate();
if (isServiceRunning()) {
stopSelf();
} else {
service = new WeakReference<>(this);
}
EventBus.getDefault().register(this);
mPrefs = getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
initViews();
}
private void initViews() {
mFloatingLayoutHierarchyView = new FloatingLayoutHierarchyView(this);
mFloatingLayoutBoundsView = new FloatingLayoutBoundsView(this);
initWindowMenu();
mWindowViewController.addView(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, true, mFloatingLayoutHierarchyView);
mWindowViewController.addView(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT, true, mFloatingLayoutBoundsView);
}
private void initWindowMenu() {
mWindowHoverMenu = (WindowHoverMenu) new HoverMenuBuilder(this)
.displayWithinWindow()
.useAdapter(new HoverMenuAdapter(this))
.restoreVisualState(loadPreferredLocation())
.build();
mWindowHoverMenu.getHoverMenuView().setContentBackgroundColor(ThemeColorManagerCompat.getColorPrimary());
mWindowHoverMenu.addOnExitListener(mWindowHoverMenuMenuExitListener);
mWindowViewController = mWindowHoverMenu.getWindowViewController();
mWindowHoverMenu.setHoverMenuTransitionListener(new SimpleHoverMenuTransitionListener() {
@Override
public void onExpanding() {
captureCurrentWindow();
}
@Override
public void onCollapsing() {
LayoutInspector.getInstance().clearCapture();
EventBus.getDefault().post(new MessageEvent(MESSAGE_MENU_COLLAPSING));
}
});
}
private void captureCurrentWindow() {
NodeInfo nodeInfo = LayoutInspector.getInstance().captureCurrentWindow();
if (nodeInfo == null)
return;
mFloatingLayoutHierarchyView.setRootNode(nodeInfo);
mFloatingLayoutBoundsView.setRootNode(nodeInfo);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!mIsRunning) {
mIsRunning = true;
mWindowHoverMenu.show();
}
return START_STICKY;
}
@Override
public void onDestroy() {
mWindowHoverMenu.hide();
mIsRunning = false;
if (isServiceRunning() && service.get() == this)
service.clear();
}
private void savePreferredLocation() {
String memento = mWindowHoverMenu.getVisualState();
mPrefs.edit().putString(PREF_HOVER_MENU_VISUAL_STATE, memento).apply();
}
private String loadPreferredLocation() {
return mPrefs.getString(PREF_HOVER_MENU_VISUAL_STATE, null);
}
@Subscribe
public void onMessageEvent(MessageEvent event) {
switch (event.message) {
case MESSAGE_SHOW_AND_EXPAND_MENU:
mWindowHoverMenu.show();
break;
case MESSAGE_SHOW_LAYOUT_HIERARCHY:
mWindowHoverMenu.hide();
showView(mFloatingLayoutHierarchyView);
break;
case MESSAGE_SHOW_LAYOUT_BOUNDS:
mWindowHoverMenu.hide();
showView(mFloatingLayoutBoundsView);
break;
case MESSAGE_COLLAPSE_MENU:
mWindowHoverMenu.collapseMenu();
break;
}
}
private void showView(View view) {
view.setVisibility(View.VISIBLE);
mWindowViewController.makeTouchable(view);
}
}

View File

@@ -0,0 +1,82 @@
package com.stardust.scriptdroid.external.floating_window.content;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.View;
import android.widget.Toast;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.floating_window.HoverMenuService;
import com.stardust.scriptdroid.layout_inspector.LayoutInspector;
import com.stardust.util.MessageEvent;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import io.mattcarroll.hover.Navigator;
import io.mattcarroll.hover.NavigatorContent;
import io.mattcarroll.hover.defaulthovermenu.menus.Menu;
import io.mattcarroll.hover.defaulthovermenu.menus.MenuAction;
import io.mattcarroll.hover.defaulthovermenu.menus.MenuItem;
import io.mattcarroll.hover.defaulthovermenu.menus.MenuListNavigatorContent;
/**
* Created by Stardust on 2017/3/12.
*/
public class MainMenuNavigatorContent implements NavigatorContent {
private View mView;
public MainMenuNavigatorContent(Context context) {
mView = View.inflate(context, R.layout.floating_window_main_menu, null);
ViewBinder.bind(this);
}
@ViewBinding.Click(R.id.layout_hierarchy)
private void showLayoutHierarchy() {
if (LayoutInspector.getInstance().getCapture() == null) {
Toast.makeText(mView.getContext(), R.string.text_no_accessibility_permission_to_capture, Toast.LENGTH_SHORT).show();
} else {
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_SHOW_LAYOUT_HIERARCHY));
}
}
@ViewBinding.Click(R.id.layout_bounds)
private void showLayoutBounds() {
if (LayoutInspector.getInstance().getCapture() == null) {
Toast.makeText(mView.getContext(), R.string.text_no_accessibility_permission_to_capture, Toast.LENGTH_SHORT).show();
} else {
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_SHOW_LAYOUT_BOUNDS));
}
}
@NonNull
@Override
public View getView() {
return mView;
}
@Override
public void onShown(@NonNull Navigator navigator) {
}
@Override
public void onHidden() {
}
public View findViewById(int id) {
return mView.findViewById(id);
}
}

View File

@@ -0,0 +1,40 @@
package com.stardust.scriptdroid.external.floating_window.content;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.View;
import android.widget.ImageView;
import com.stardust.scriptdroid.R;
import io.mattcarroll.hover.Navigator;
import io.mattcarroll.hover.NavigatorContent;
/**
* Created by Stardust on 2017/3/12.
*/
public class RecordNavigatorContent implements NavigatorContent {
private View mView;
public RecordNavigatorContent(Context context) {
mView = new View(context);
}
@NonNull
@Override
public View getView() {
return mView;
}
@Override
public void onShown(@NonNull Navigator navigator) {
}
@Override
public void onHidden() {
}
}

View File

@@ -0,0 +1,43 @@
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.R;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
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(SharedPrefScriptFileList.getInstance());
}
@NonNull
@Override
public View getView() {
return mFloatingScriptFileListView;
}
@Override
public void onShown(@NonNull Navigator navigator) {
}
@Override
public void onHidden() {
}
}

View File

@@ -0,0 +1,75 @@
package com.stardust.scriptdroid.external.floating_window.view;
import android.content.Context;
import android.graphics.Color;
import android.view.KeyEvent;
import android.view.WindowManager;
import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.Theme;
import com.stardust.scriptdroid.external.floating_window.HoverMenuService;
import com.stardust.scriptdroid.layout_inspector.NodeInfo;
import com.stardust.scriptdroid.layout_inspector.view.LayoutBoundsView;
import com.stardust.scriptdroid.layout_inspector.view.NodeInfoView;
import com.stardust.scriptdroid.layout_inspector.view.OnNodeInfoSelectListener;
import com.stardust.util.MessageEvent;
import org.greenrobot.eventbus.EventBus;
/**
* Created by Stardust on 2017/3/12.
*/
public class FloatingLayoutBoundsView extends LayoutBoundsView {
private MaterialDialog mNodeInfoDialog;
private NodeInfoView mNodeInfoView;
public FloatingLayoutBoundsView(Context context) {
super(context);
init();
}
private void init() {
setOnNodeInfoSelectListener(new OnNodeInfoSelectListener() {
@Override
public void onNodeSelect(NodeInfo info) {
showNodeInfo(info);
}
});
setBackgroundColor(0x66000000);
setVisibility(GONE);
getPaint().setColor(0xFF222222);
getPaint().setStrokeWidth(2f);
}
private void showNodeInfo(NodeInfo info) {
ensureDialog();
mNodeInfoView.setNodeInfo(info);
mNodeInfoDialog.show();
}
private void ensureDialog() {
if (mNodeInfoDialog == null) {
mNodeInfoView = new NodeInfoView(getContext());
mNodeInfoDialog = new MaterialDialog.Builder(getContext())
.customView(mNodeInfoView, false)
.theme(Theme.LIGHT)
.build();
mNodeInfoDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_SHOW_AND_EXPAND_MENU));
setVisibility(GONE);
return true;
}
return super.dispatchKeyEvent(event);
}
}

View File

@@ -0,0 +1,79 @@
package com.stardust.scriptdroid.external.floating_window.view;
import android.content.Context;
import android.content.DialogInterface;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.Theme;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.floating_window.HoverMenuService;
import com.stardust.scriptdroid.layout_inspector.NodeInfo;
import com.stardust.scriptdroid.layout_inspector.view.LayoutHierarchyView;
import com.stardust.scriptdroid.layout_inspector.view.NodeInfoView;
import com.stardust.scriptdroid.layout_inspector.view.OnNodeInfoSelectListener;
import com.stardust.util.MessageEvent;
import org.greenrobot.eventbus.EventBus;
/**
* Created by Stardust on 2017/3/12.
*/
public class FloatingLayoutHierarchyView extends LayoutHierarchyView {
private static final String TAG = "FloatingHierarchyView";
private MaterialDialog mNodeInfoDialog;
private NodeInfoView mNodeInfoView;
public FloatingLayoutHierarchyView(Context context) {
super(context);
init();
}
private void init() {
setBackgroundColor(0x99ffffff);
setVisibility(GONE);
setShowClickedNodeBounds(true);
getBoundsPaint().setStrokeWidth(3);
getBoundsPaint().setColor(0xFFD32F2F);
setOnNodeInfoLongClickListener(new OnNodeInfoSelectListener() {
@Override
public void onNodeSelect(NodeInfo info) {
showNodeInfo(info);
}
});
}
private void showNodeInfo(NodeInfo info) {
ensureDialog();
mNodeInfoView.setNodeInfo(info);
mNodeInfoDialog.show();
}
private void ensureDialog() {
if (mNodeInfoDialog == null) {
mNodeInfoView = new NodeInfoView(getContext());
mNodeInfoDialog = new MaterialDialog.Builder(getContext())
.customView(mNodeInfoView, false)
.theme(Theme.LIGHT)
.build();
mNodeInfoDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
}
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_SHOW_AND_EXPAND_MENU));
setVisibility(GONE);
return true;
}
return super.dispatchKeyEvent(event);
}
}

View File

@@ -0,0 +1,215 @@
package com.stardust.scriptdroid.external.floating_window.view;
import android.content.Context;
import android.os.Environment;
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;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.external.floating_window.HoverMenuService;
import com.stardust.scriptdroid.tool.ViewTool;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperation;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperationPopupMenu;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.util.MessageEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Stardust on 2017/3/12.
*/
public class FloatingScriptFileListView extends RecyclerView {
private ScriptFileList mScriptFileList;
private final OnClickListener mOnItemClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
int position = getChildViewHolder(v).getAdapterPosition();
onItemClicked(v, position);
}
};
private final OnClickListener mOnEditIconClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
int position = getChildViewHolder((View) v.getParent()).getAdapterPosition();
onEditIconClick(v, position);
}
};
private final OnClickListener mOnMoreIconClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
mOperateFileIndex = getChildViewHolder((View) v.getParent()).getAdapterPosition();
showOrDismissOperationPopupMenu(v);
}
};
private ScriptFileOperationPopupMenu mScriptFileOperationPopupMenu;
private int mOperateFileIndex;
public FloatingScriptFileListView(Context context) {
super(context);
init();
}
private void init() {
setAdapter(new Adapter());
setLayoutManager(new LinearLayoutManager(getContext()));
addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL));
initScriptFileOperationPopupMenu();
}
@Subscribe
public void showMessage(ScriptFileOperation.ShowMessageEvent event) {
Toast.makeText(getContext(), event.messageResId, Toast.LENGTH_SHORT).show();
}
private void initScriptFileOperationPopupMenu() {
mScriptFileOperationPopupMenu = new ScriptFileOperationPopupMenu(getContext(), getScriptFileOperations());
mScriptFileOperationPopupMenu.setOnItemClickListener(new ScriptFileOperationPopupMenu.OnItemClickListener() {
@Override
public void onClick(View view, int position, ScriptFileOperation operation) {
operation.operate(FloatingScriptFileListView.this, mScriptFileList, mOperateFileIndex);
mScriptFileOperationPopupMenu.dismiss();
if (!(operation instanceof ScriptFileOperation.Rename))
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU));
}
});
}
protected List<ScriptFileOperation> getScriptFileOperations() {
List<ScriptFileOperation> scriptFileOperations = new ArrayList<>();
scriptFileOperations.add(new ScriptFileOperation.Run());
scriptFileOperations.add(new ScriptFileOperation.Rename() {
@Override
public void operate(final RecyclerView recyclerView, final ScriptFileList scriptFileList, final int position) {
String oldName = scriptFileList.get(position).name;
MaterialDialog dialog = new ThemeColorMaterialDialogBuilder(recyclerView.getContext())
.title(R.string.text_rename)
.checkBoxPrompt(App.getApp().getString(R.string.text_rename_file_meanwhile), false, null)
.input(App.getApp().getString(R.string.text_please_input_new_name), oldName, new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
scriptFileList.rename(position, input.toString(), dialog.isPromptCheckBoxChecked());
recyclerView.getAdapter().notifyItemChanged(position);
}
})
.build();
dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
dialog.show();
}
});
scriptFileOperations.add(new ScriptFileOperation.OpenByOtherApp());
scriptFileOperations.add(new ScriptFileOperation.CreateShortcut());
scriptFileOperations.add(new ScriptFileOperation.Remove());
scriptFileOperations.add(new ScriptFileOperation.Delete());
return scriptFileOperations;
}
protected void onItemClicked(View v, int position) {
new ScriptFileOperation.Run().operate(this, mScriptFileList, position);
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU));
}
protected void onEditIconClick(View v, int position) {
new ScriptFileOperation.Edit().operate(this, mScriptFileList, position);
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU));
}
public void setScriptFileList(ScriptFileList scriptFileList) {
mScriptFileList = scriptFileList;
getAdapter().notifyDataSetChanged();
}
private void showOrDismissOperationPopupMenu(View v) {
if (mScriptFileOperationPopupMenu.isShowing()) {
mScriptFileOperationPopupMenu.dismiss();
} else {
mScriptFileOperationPopupMenu.show(v);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
EventBus.getDefault().register(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
EventBus.getDefault().unregister(this);
}
@Subscribe
public void onMenuCollapsing(MessageEvent event) {
if (event.message.equals(HoverMenuService.MESSAGE_MENU_COLLAPSING) && mScriptFileOperationPopupMenu.isShowing()) {
mScriptFileOperationPopupMenu.dismiss();
}
}
private class Adapter extends RecyclerView.Adapter<ViewHolder> {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(getContext()).inflate(R.layout.floating_script_list_recycler_view_item, parent, false);
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
ScriptFile scriptFile = mScriptFileList.get(position);
holder.name.setText(scriptFile.name);
holder.path.setText(trimFilePath(scriptFile.path));
}
private final String SD_CARD_PATH = Environment.getExternalStorageDirectory().toString();
private String trimFilePath(String path) {
if (path.startsWith(SD_CARD_PATH)) {
path = path.substring(SD_CARD_PATH.length());
}
return path;
}
@Override
public int getItemCount() {
return mScriptFileList.size();
}
}
private class ViewHolder extends RecyclerView.ViewHolder {
TextView name, path;
ViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
path = (TextView) itemView.findViewById(R.id.path);
ViewTool.$(itemView, R.id.edit).setOnClickListener(mOnEditIconClickListener);
ViewTool.$(itemView, R.id.more).setOnClickListener(mOnMoreIconClickListener);
itemView.setOnClickListener(mOnItemClickListener);
}
}
}

View File

@@ -1,97 +0,0 @@
package com.stardust.scriptdroid.external.notification.bounds_assist;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistant;
import com.stardust.util.StateObserver;
/**
* Created by Stardust on 2017/2/2.
*/
public class BoundsAssistSwitchNotification {
private static final int NOTIFY_ID = 11126;
public static final String KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE = "KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE";
private static boolean enable = false;
public static boolean isEnable() {
return enable;
}
static {
App.getStateObserver().register(KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE, new StateObserver.OnStateChangedListener() {
@Override
public void onStateChanged(boolean newState) {
setEnable(newState);
}
@Override
public void initState(boolean state) {
enable = state;
if (enable) {
showNotification();
}
}
});
App.getStateObserver().register(BoundsAssistant.KEY_BOUNDS_ASSIST_ENABLE, new StateObserver.OnStateChangedListener() {
@Override
public void onStateChanged(boolean newState) {
if (enable) {
setEnable(false);
setEnable(true);
}
}
@Override
public void initState(boolean state) {
}
});
}
public static void setEnable(boolean enable) {
if (BoundsAssistSwitchNotification.enable == enable)
return;
BoundsAssistSwitchNotification.enable = enable;
PreferenceManager.getDefaultSharedPreferences(App.getApp()).edit().putBoolean(KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE, enable).apply();
if (enable) {
showNotification();
} else {
hideNotification();
}
}
private static void showNotification() {
Notification notification = new NotificationCompat.Builder(App.getApp())
.setAutoCancel(false)
.setSmallIcon(R.drawable.ic_robot_head)
.setDeleteIntent(BoundsAssistSwitchNotificationHandleService.getDeletePendingIntent())
.setContentText(BoundsAssistant.isAssistModeEnable() ?
App.getApp().getString(R.string.text_assist_mode_enabled) :
App.getApp().getString(R.string.text_assist_mode_disabled))
.setContentIntent(BoundsAssistSwitchNotificationHandleService.getStartIntent())
.build();
showNotification(notification);
}
private static void showNotification(Notification notification) {
NotificationManager notificationManager = (NotificationManager) App.getApp().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFY_ID, notification);
}
private static void hideNotification() {
NotificationManager notificationManager = (NotificationManager) App.getApp().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFY_ID);
}
}

View File

@@ -1,67 +0,0 @@
package com.stardust.scriptdroid.external.notification.bounds_assist;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistant;
import static com.stardust.scriptdroid.external.notification.bounds_assist.BoundsAssistSwitchNotification.KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE;
/**
* Created by Stardust on 2017/2/2.
*/
public class BoundsAssistSwitchNotificationHandleService extends Service {
private static final String EXTRA_INTENT_VALID = "BoundsAssistSwitchNotificationHandleService.intentValid";
private static final String EXTRA_ACTION = "action";
private static final int ACTION_TOGGLE_ASSIST_MODE = 1;
private static final int ACTION_CANCEL_ASSIST_MODE_NOTIFICATION = 2;
public static PendingIntent getStartIntent() {
Intent intent = new Intent(App.getApp(), BoundsAssistSwitchNotificationHandleService.class)
.putExtra(EXTRA_INTENT_VALID, true)
.putExtra(EXTRA_ACTION, ACTION_TOGGLE_ASSIST_MODE);
return PendingIntent.getService(App.getApp(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
boolean intentValid = intent.getBooleanExtra(EXTRA_INTENT_VALID, false);
if (intentValid) {
performAction(intent.getIntExtra(EXTRA_ACTION, 0));
}
stopSelf();
return super.onStartCommand(intent, flags, startId);
}
private void performAction(int action) {
switch (action) {
case ACTION_TOGGLE_ASSIST_MODE:
BoundsAssistant.setAssistModeEnable(!BoundsAssistant.isAssistModeEnable());
break;
case ACTION_CANCEL_ASSIST_MODE_NOTIFICATION:
App.getStateObserver().setState(KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE, false);
break;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
public static PendingIntent getDeletePendingIntent() {
Intent deleteIntent = new Intent(App.getApp(), BoundsAssistSwitchNotificationHandleService.class)
.putExtra(EXTRA_INTENT_VALID, true)
.putExtra(EXTRA_ACTION, ACTION_CANCEL_ASSIST_MODE_NOTIFICATION);
return PendingIntent.getService(App.getApp(), 0, deleteIntent, PendingIntent.FLAG_ONE_SHOT);
}
}

View File

@@ -8,9 +8,9 @@ import android.os.IBinder;
import android.support.annotation.Nullable;
import android.widget.Toast;
import com.stardust.scriptdroid.record.AccessibilityRecorderDelegate;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.record.AccessibilityRecorderDelegate;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.service.VolumeChangeListenService;
import com.stardust.scriptdroid.ui.main.MainActivity;

View File

@@ -7,8 +7,8 @@ import android.text.TextUtils;
import android.widget.Toast;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.ui.edit.EditActivity;
import com.stardust.scriptdroid.R;
/**
* Created by Stardust on 2017/2/2.

View File

@@ -8,12 +8,12 @@ import android.text.TextUtils;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.ui.main.MainActivity;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.scriptdroid.R;
import java.io.File;

View File

@@ -7,8 +7,8 @@ import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.widget.Toast;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.shortcut.ShortcutActivity;
import com.stardust.scriptdroid.R;
/**
* Created by Stardust on 2017/2/22.

View File

@@ -6,8 +6,8 @@ import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Toast;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.R;
import java.io.File;
import java.util.LinkedList;

View File

@@ -1,32 +0,0 @@
package com.stardust.scriptdroid.external.tile;
import android.os.Build;
import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.support.annotation.RequiresApi;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistant;
/**
* Created by Stardust on 2017/1/26.
*/
@RequiresApi(api = Build.VERSION_CODES.N)
public class BoundsAssistEnableTileService extends TileService {
public void onClick() {
BoundsAssistant.setAssistModeEnable(!BoundsAssistant.isAssistModeEnable());
updateTile();
}
private void updateTile() {
getQsTile().setState(BoundsAssistant.isAssistModeEnable() ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE);
getQsTile().updateTile();
}
@Override
public void onStartListening() {
updateTile();
}
}

View File

@@ -2,13 +2,13 @@ package com.stardust.scriptdroid.file;
import android.content.Context;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
import com.stardust.util.MapEntries;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import java.util.LinkedHashMap;
import java.util.Map;

View File

@@ -0,0 +1,53 @@
package com.stardust.scriptdroid.layout_inspector;
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.view.accessibility.AccessibilityDelegate;
/**
* Created by Stardust on 2017/3/10.
*/
public class LayoutInspector implements AccessibilityDelegate {
private static LayoutInspector instance = new LayoutInspector();
public static LayoutInspector getInstance() {
return instance;
}
private AccessibilityNodeInfo mRootInActiveWindow;
private NodeInfo mCapture;
@Override
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
mRootInActiveWindow = service.getRootInActiveWindow();
return false;
}
public AccessibilityNodeInfo getRootInActiveWindow() {
return mRootInActiveWindow;
}
public NodeInfo captureCurrentWindow() {
if (mRootInActiveWindow == null) {
mCapture = null;
} else {
mCapture = NodeInfo.capture(mRootInActiveWindow);
}
return mCapture;
}
public void clearCapture() {
mRootInActiveWindow = null;
mCapture = null;
}
public NodeInfo getCapture() {
return mCapture;
}
}

View File

@@ -0,0 +1,88 @@
package com.stardust.scriptdroid.layout_inspector;
import android.graphics.Rect;
import android.support.annotation.NonNull;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.view.accessibility.AccessibilityNodeInfo;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Stardust on 2017/3/10.
*/
public class NodeInfo {
private List<NodeInfo> children = new ArrayList<>();
public String id;
public CharSequence contentDesc, className, packageName, text;
public int drawingOrder;
public boolean accessibilityFocused, checked, clickable, contextClickable, dismissable, editable, enabled,
focusable, longClickable, selected, scrollable, visibleToUser;
public String bounds;
private Rect mBoundsInScreen;
public NodeInfo(AccessibilityNodeInfoCompat node) {
id = simplifyId(node.getViewIdResourceName());
contentDesc = node.getContentDescription();
className = node.getClassName();
packageName = node.getPackageName();
text = node.getText();
drawingOrder = node.getDrawingOrder();
accessibilityFocused = node.isAccessibilityFocused();
checked = node.isChecked();
clickable = node.isClickable();
contextClickable = node.isContextClickable();
dismissable = node.isDismissable();
enabled = node.isEnabled();
editable = node.isEditable();
focusable = node.isFocusable();
longClickable = node.isLongClickable();
selected = node.isSelected();
scrollable = node.isScrollable();
visibleToUser = node.isVisibleToUser();
mBoundsInScreen = new Rect();
node.getBoundsInScreen(mBoundsInScreen);
bounds = boundsToString(mBoundsInScreen);
}
private String simplifyId(String idResourceName) {
if(idResourceName == null)
return null;
int i = idResourceName.indexOf('/');
return idResourceName.substring(i + 1);
}
public Rect getBoundsInScreen() {
return mBoundsInScreen;
}
public static String boundsToString(Rect rect) {
return rect.toString().replace('-', ',').replace(" ", "").substring(4);
}
public NodeInfo(AccessibilityNodeInfo node) {
this(new AccessibilityNodeInfoCompat(node));
}
public static NodeInfo capture(@NonNull AccessibilityNodeInfo root) {
NodeInfo nodeInfo = new NodeInfo(root);
for (int i = 0; i < root.getChildCount(); i++) {
AccessibilityNodeInfo child = root.getChild(i);
if (child != null) {
nodeInfo.children.add(capture(child));
}
}
return nodeInfo;
}
public List<NodeInfo> getChildren() {
return children;
}
}

View File

@@ -0,0 +1,121 @@
package com.stardust.scriptdroid.layout_inspector.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import com.stardust.scriptdroid.layout_inspector.NodeInfo;
import com.stardust.scriptdroid.tool.ViewTool;
/**
* Created by Stardust on 2017/3/10.
*/
public class LayoutBoundsView extends View {
private NodeInfo mRootNode;
private Paint mPaint;
private int mStatusBarHeight;
private OnNodeInfoSelectListener mOnNodeInfoSelectListener;
public LayoutBoundsView(Context context) {
super(context);
init();
}
public LayoutBoundsView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public LayoutBoundsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public LayoutBoundsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
public void setOnNodeInfoSelectListener(OnNodeInfoSelectListener onNodeInfoSelectListener) {
mOnNodeInfoSelectListener = onNodeInfoSelectListener;
}
public void setRootNode(NodeInfo rootNode) {
mRootNode = rootNode;
}
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.STROKE);
mStatusBarHeight = ViewTool.getStatusBarHeight(getContext());
setWillNotDraw(false);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
draw(canvas, mRootNode);
}
public Paint getPaint() {
return mPaint;
}
private void draw(Canvas canvas, NodeInfo node) {
if (node == null)
return;
drawRect(canvas, node.getBoundsInScreen(), mStatusBarHeight, mPaint);
for (NodeInfo child : node.getChildren()) {
draw(canvas, child);
}
}
static void drawRect(Canvas canvas, Rect rect, int statusBarHeight, Paint paint) {
Rect offsetRect = new Rect(rect);
offsetRect.offset(0, -statusBarHeight);
canvas.drawRect(offsetRect, paint);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN && mRootNode != null) {
NodeInfo nodeInfo = findNodeAt(mRootNode, (int) event.getRawX(), (int) event.getRawY());
onNodeInfoClick(nodeInfo);
}
return super.onTouchEvent(event);
}
private void onNodeInfoClick(NodeInfo nodeInfo) {
if (mOnNodeInfoSelectListener != null) {
mOnNodeInfoSelectListener.onNodeSelect(nodeInfo);
}
}
private NodeInfo findNodeAt(NodeInfo node, int x, int y) {
for (NodeInfo child : node.getChildren()) {
if (child != null && child.getBoundsInScreen().contains(x, y)) {
return findNodeAt(child, x, y);
}
}
return node;
}
}

View File

@@ -0,0 +1,210 @@
package com.stardust.scriptdroid.layout_inspector.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.layout_inspector.NodeInfo;
import com.stardust.scriptdroid.tool.ViewTool;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import pl.openrnd.multilevellistview.ItemInfo;
import pl.openrnd.multilevellistview.MultiLevelListAdapter;
import pl.openrnd.multilevellistview.MultiLevelListView;
import pl.openrnd.multilevellistview.NestType;
import pl.openrnd.multilevellistview.OnItemClickListener;
/**
* Created by Stardust on 2017/3/10.
*/
public class LayoutHierarchyView extends MultiLevelListView {
private Adapter mAdapter;
private OnNodeInfoSelectListener mOnNodeInfoSelectListener;
private AdapterView.OnItemLongClickListener mOnItemLongClickListenerProxy = new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (mOnNodeInfoSelectListener != null) {
mOnNodeInfoSelectListener.onNodeSelect(((ViewHolder) view.getTag()).nodeInfo);
return true;
}
return false;
}
};
private Paint mPaint;
private int mStatusBarHeight;
private NodeInfo mClickedNodeInfo;
private boolean mShowClickedNodeBounds;
public LayoutHierarchyView(Context context) {
super(context);
init();
}
public LayoutHierarchyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LayoutHierarchyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public void setShowClickedNodeBounds(boolean showClickedNodeBounds) {
mShowClickedNodeBounds = showClickedNodeBounds;
}
private void init() {
mAdapter = new Adapter();
setAdapter(mAdapter);
setNestType(NestType.MULTIPLE);
((ListView) getChildAt(0)).setOnItemLongClickListener(mOnItemLongClickListenerProxy);
setWillNotDraw(false);
initPaint();
setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClicked(MultiLevelListView parent, View view, Object item, ItemInfo itemInfo) {
mClickedNodeInfo = (NodeInfo) item;
invalidate();
}
@Override
public void onGroupItemClicked(MultiLevelListView parent, View view, Object item, ItemInfo itemInfo) {
mClickedNodeInfo = (NodeInfo) item;
invalidate();
}
});
}
private void initPaint() {
mPaint = new Paint();
mPaint.setColor(Color.DKGRAY);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(3);
mStatusBarHeight = ViewTool.getStatusBarHeight(getContext());
}
public Paint getBoundsPaint() {
return mPaint;
}
public void setRootNode(NodeInfo rootNodeInfo) {
mAdapter.setDataItems(Collections.singletonList(rootNodeInfo));
mClickedNodeInfo = null;
}
public void setOnNodeInfoLongClickListener(final OnNodeInfoSelectListener onNodeInfoSelectListener) {
mOnNodeInfoSelectListener = onNodeInfoSelectListener;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mShowClickedNodeBounds && mClickedNodeInfo != null) {
LayoutBoundsView.drawRect(canvas, mClickedNodeInfo.getBoundsInScreen(), mStatusBarHeight, mPaint);
}
}
private class ViewHolder {
TextView nameView;
TextView infoView;
ImageView arrowView;
LevelBeamView levelBeamView;
NodeInfo nodeInfo;
ViewHolder(View view) {
infoView = (TextView) view.findViewById(R.id.dataItemInfo);
nameView = (TextView) view.findViewById(R.id.dataItemName);
arrowView = (ImageView) view.findViewById(R.id.dataItemArrow);
levelBeamView = (LevelBeamView) view.findViewById(R.id.dataItemLevelBeam);
}
}
private class Adapter extends MultiLevelListAdapter {
@Override
public List<?> getSubObjects(Object object) {
return ((NodeInfo) object).getChildren();
}
@Override
public boolean isExpandable(Object object) {
return !((NodeInfo) object).getChildren().isEmpty();
}
@Override
public View getViewForObject(Object object, View convertView, ItemInfo itemInfo) {
NodeInfo nodeInfo = (NodeInfo) object;
ViewHolder viewHolder;
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.layout_hierarchy_view_item, null);
viewHolder = new ViewHolder(convertView);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.nameView.setText(simplifyClassName(nodeInfo.className));
viewHolder.nodeInfo = nodeInfo;
if (viewHolder.infoView.getVisibility() == VISIBLE)
viewHolder.infoView.setText(getItemInfoDsc(itemInfo));
if (itemInfo.isExpandable() && !isAlwaysExpanded()) {
viewHolder.arrowView.setVisibility(View.VISIBLE);
viewHolder.arrowView.setImageResource(itemInfo.isExpanded() ?
R.drawable.arrow_up : R.drawable.arrow_down);
} else {
viewHolder.arrowView.setVisibility(View.GONE);
}
viewHolder.levelBeamView.setLevel(itemInfo.getLevel());
return convertView;
}
private String simplifyClassName(CharSequence className) {
String s = className.toString();
if (s.startsWith("android.widget.")) {
s = s.substring(15);
}
return s;
}
private String getItemInfoDsc(ItemInfo itemInfo) {
StringBuilder builder = new StringBuilder();
builder.append(String.format(Locale.getDefault(), "level[%d], idx in level[%d/%d]",
itemInfo.getLevel() + 1, /*Indexing starts from 0*/
itemInfo.getIdxInLevel() + 1 /*Indexing starts from 0*/,
itemInfo.getLevelSize()));
if (itemInfo.isExpandable()) {
builder.append(String.format(", expanded[%b]", itemInfo.isExpanded()));
}
return builder.toString();
}
}
}

View File

@@ -0,0 +1,92 @@
package com.stardust.scriptdroid.layout_inspector.view;
import android.content.Context;
import android.os.Build;
import android.support.annotation.AttrRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.annotation.StyleRes;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ViewSwitcher;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.layout_inspector.LayoutInspector;
import com.stardust.scriptdroid.layout_inspector.NodeInfo;
/**
* Created by Stardust on 2017/3/10.
*/
public class LayoutInspectView extends FrameLayout {
private ViewSwitcher mViewSwitcher;
private NodeInfoView mNodeInfoView;
private LayoutHierarchyView mLayoutBoundsView;
private View mCurrentView;
public LayoutInspectView(@NonNull Context context) {
super(context);
init();
}
public LayoutInspectView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public LayoutInspectView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public LayoutInspectView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
inflate(getContext(), R.layout.floating_window_expand, this);
mNodeInfoView = (NodeInfoView) findViewById(R.id.node_info);
mLayoutBoundsView = (LayoutHierarchyView) findViewById(R.id.bounds_view);
mViewSwitcher = (ViewSwitcher) findViewById(R.id.view_switcher);
mCurrentView = mNodeInfoView;
mLayoutBoundsView.setOnNodeInfoLongClickListener(new OnNodeInfoSelectListener() {
@Override
public void onNodeSelect(NodeInfo info) {
mNodeInfoView.setNodeInfo(info);
showNodeInfoView();
}
});
}
public void showNodeInfoView() {
if (mCurrentView != mNodeInfoView) {
mViewSwitcher.showPrevious();
mCurrentView = mNodeInfoView;
}
}
public void showLayoutBoundsView() {
mLayoutBoundsView.setRootNode(LayoutInspector.getInstance().captureCurrentWindow());
backToLayoutBoundsView();
}
public boolean isLayoutBoundsViewShowing() {
return mCurrentView == mLayoutBoundsView;
}
public boolean isNodeInfoViewShowing() {
return mCurrentView == mNodeInfoView;
}
public void backToLayoutBoundsView() {
if (mCurrentView != mLayoutBoundsView) {
mViewSwitcher.showNext();
mCurrentView = mLayoutBoundsView;
}
}
}

View File

@@ -0,0 +1,97 @@
package com.stardust.scriptdroid.layout_inspector.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import com.stardust.scriptdroid.R;
/**
* Created by Stardust on 2017/3/10.
*/
public class LevelBeamView extends View {
private static final String TAG = "LevelBeamView";
private static final int[] colors = {
0xff1abc9c,
0xff3498db,
0xffe67e22,
0xff8e44ad,
0xfff1c40f,
0xff2ecc71,
};
private int mLevel;
private int mPaddingLeft, mPaddingRight;
private int mLinesWidth;
private int mLinesOffset;
private Paint mLinePaint;
public LevelBeamView(Context context) {
super(context);
init();
}
public LevelBeamView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public LevelBeamView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public void setLevel(int level) {
mLevel = level;
requestLayout();
}
private void init() {
setWillNotDraw(false);
mPaddingLeft = (int) getResources().getDimension(R.dimen.level_beam_view_padding_left);
mPaddingRight = (int) getResources().getDimension(R.dimen.level_beam_view_padding_right);
mLinesWidth = (int) getResources().getDimension(R.dimen.level_beam_view_line_width);
mLinesOffset = (int) getResources().getDimension(R.dimen.level_beam_view_line_offset);
mLinePaint = new Paint();
mLinePaint.setAntiAlias(true);
mLinePaint.setColor(Color.RED);
mLinePaint.setStyle(Paint.Style.FILL);
mLinePaint.setStrokeWidth(mLinesWidth);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = mPaddingLeft + mPaddingRight + (mLevel + 1) * (mLinesWidth + mLinesOffset);
int height = View.MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
Log.i(TAG, "onDraw");
super.onDraw(canvas);
for (int lvl = 0; lvl <= mLevel; lvl++) {
float LINE_X = mPaddingLeft + lvl * mLinesWidth;
if (lvl >= 1) {
LINE_X += lvl * mLinesOffset;
}
mLinePaint.setColor(getColorForLevel(lvl));
canvas.drawLine(LINE_X, 0, LINE_X, canvas.getHeight(), mLinePaint);
}
}
private int getColorForLevel(int level) {
return colors[level % colors.length];
}
}

View File

@@ -0,0 +1,115 @@
package com.stardust.scriptdroid.layout_inspector.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.layout_inspector.NodeInfo;
import com.stardust.scriptdroid.tool.ClipboardTool;
import java.lang.reflect.Field;
import de.codecrafters.tableview.TableDataAdapter;
import de.codecrafters.tableview.TableView;
import de.codecrafters.tableview.model.TableColumnWeightModel;
import de.codecrafters.tableview.toolkit.SimpleTableDataAdapter;
import de.codecrafters.tableview.toolkit.SimpleTableHeaderAdapter;
import de.codecrafters.tableview.toolkit.TableDataRowBackgroundProviders;
/**
* Created by Stardust on 2017/3/10.
*/
public class NodeInfoView extends TableView {
private static final Field[] fields = NodeInfo.class.getFields();
private String[][] mData = new String[fields.length][2];
public NodeInfoView(Context context) {
super(context);
init();
}
public NodeInfoView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public NodeInfoView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public void setNodeInfo(NodeInfo nodeInfo) {
for (int i = 0; i < fields.length; i++) {
try {
Object value = fields[i].get(nodeInfo);
mData[i][1] = value == null ? "null" : value.toString();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
getDataAdapter().notifyDataSetChanged();
}
private void init() {
initData();
setUpTableConfig();
setAdapter();
setUpStyle();
}
private void setAdapter() {
setDataAdapter(new TableDataAdapter<String[]>(getContext(), mData) {
SimpleTableDataAdapter mSimpleTableDataAdapter = new SimpleTableDataAdapter(getContext(), mData);
@Override
public View getCellView(int rowIndex, int columnIndex, ViewGroup parentView) {
final TextView textView = (TextView) mSimpleTableDataAdapter.getCellView(rowIndex, columnIndex, parentView);
textView.setSingleLine(false);
textView.setMaxLines(3);
textView.setTextColor(0xcc000000);
textView.setTextIsSelectable(true);
textView.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
ClipboardTool.setClip(textView.getText());
Toast.makeText(getContext(), R.string.text_already_copy_to_clip, Toast.LENGTH_SHORT).show();
return true;
}
});
return textView;
}
});
}
private void setUpTableConfig() {
setColumnCount(2);
setColumnModel(new TableColumnWeightModel(2));
}
private void initData() {
for (int i = 0; i < mData.length; i++) {
mData[i][0] = fields[i].getName();
mData[i][1] = "";
}
}
private void setUpStyle() {
int colorEvenRows = Color.WHITE;
int colorOddRows = 0xffe7e7e7;
setDataRowBackgroundProvider(TableDataRowBackgroundProviders.alternatingRowColors(colorEvenRows, colorOddRows));
getChildAt(0).setVisibility(GONE);
}
}

View File

@@ -0,0 +1,12 @@
package com.stardust.scriptdroid.layout_inspector.view;
import com.stardust.scriptdroid.layout_inspector.NodeInfo;
/**
* Created by Stardust on 2017/3/10.
*/
public interface OnNodeInfoSelectListener {
void onNodeSelect(NodeInfo info);
}

View File

@@ -3,7 +3,7 @@ package com.stardust.scriptdroid.record;
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
import com.stardust.scriptdroid.service.AccessibilityDelegate;
import com.stardust.view.accessibility.AccessibilityDelegate;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.PAUSED;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.RECORDING;

View File

@@ -7,13 +7,12 @@ import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.scriptdroid.droid.runtime.action.FilterAction;
import com.stardust.scriptdroid.layout_inspector.NodeInfo;
import com.stardust.util.SparseArrayEntries;
import com.stardust.view.accessibility.AccessibilityNodeInfoHelper;
import java.util.List;
import static com.stardust.scriptdroid.bounds_assist.BoundsAssistant.boundsToString;
import static com.stardust.scriptdroid.bounds_assist.BoundsAssistant.getBoundsInScreen;
/**
* Created by Stardust on 2017/2/14.
@@ -69,7 +68,7 @@ public class ActionRecorder {
AccessibilityNodeInfo source = event.getSource();
if (source == null)
return;
String bounds = boundsToString(getBoundsInScreen(source));
String bounds = NodeInfo.boundsToString(AccessibilityNodeInfoHelper.getBoundsInScreen(source));
source.recycle();
onAccessibilityEvent(event, bounds, sb);
}
@@ -130,7 +129,7 @@ public class ActionRecorder {
private static int findInEditableList(List<AccessibilityNodeInfo> editableList, AccessibilityNodeInfo editable) {
int i = 0;
for (AccessibilityNodeInfo nodeInfo : editableList) {
if (getBoundsInScreen(nodeInfo).equals(getBoundsInScreen(editable))) {
if (AccessibilityNodeInfoHelper.getBoundsInScreen(nodeInfo).equals(AccessibilityNodeInfoHelper.getBoundsInScreen(editable))) {
return i;
}
i++;

View File

@@ -1,20 +1,12 @@
package com.stardust.scriptdroid.record.inputevent;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import android.util.Pair;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.tool.Shell;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jackpal.androidterm.ShellTermSession;
import jackpal.androidterm.emulatorview.TermSession;

View File

@@ -2,7 +2,6 @@ package com.stardust.scriptdroid.record.inputevent;
import android.support.annotation.NonNull;
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
import com.stardust.util.MapEntries;
import java.util.HashMap;

View File

@@ -3,8 +3,6 @@ package com.stardust.scriptdroid.record.inputevent;
import android.support.annotation.NonNull;
import java.text.DecimalFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**

View File

@@ -1,14 +0,0 @@
package com.stardust.scriptdroid.service;
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
/**
* Created by Stardust on 2017/2/14.
*/
public interface AccessibilityDelegate {
boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event);
}

View File

@@ -5,7 +5,9 @@ import android.os.Build;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import com.stardust.view.accessibility.AccessibilityDelegate;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import java.lang.ref.WeakReference;
@@ -79,6 +81,7 @@ public class AccessibilityWatchDogService extends AccessibilityService {
Log.v(TAG, "onAccessibilityEvent: " + event);
synchronized (mDelegates) {
for (Map.Entry<Integer, AccessibilityDelegate> entry : mDelegates.entrySet()) {
Log.v(TAG, "delegate: " + entry.getValue().getClass().getName());
if (entry.getValue().onAccessibilityEvent(this, event))
break;
}
@@ -101,7 +104,7 @@ public class AccessibilityWatchDogService extends AccessibilityService {
if (instance != null && instance.get() != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
instance.get().disableSelf();
} else {
AccessibilityServiceUtils.goToAccessibilitySetting(App.getApp());
AccessibilityServiceTool.goToAccessibilitySetting();
}
}

View File

@@ -8,9 +8,9 @@ import android.content.IntentFilter;
import android.os.IBinder;
import android.support.annotation.Nullable;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchHandleService;
import com.stardust.scriptdroid.record.AccessibilityRecorderDelegate;
import com.stardust.scriptdroid.Pref;
import java.lang.ref.WeakReference;

View File

@@ -1,19 +1,16 @@
package com.stardust.scriptdroid.tool;
import android.accessibilityservice.AccessibilityService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.provider.Settings;
import android.text.TextUtils;
import android.widget.Toast;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import static com.stardust.view.accessibility.AccessibilityServiceUtils.isAccessibilityServiceEnabled;
/**
* Created by Stardust on 2017/1/26.
*/
@@ -22,12 +19,37 @@ public class AccessibilityServiceTool {
public static void enableAccessibilityService() {
if (Pref.def().getBoolean(App.getApp().getString(R.string.key_enable_accessibility_service_by_root), false)) {
if (!AccessibilityServiceUtils.enableAccessibilityServiceByRootAndWaitFor(App.getApp(), AccessibilityWatchDogService.class, 3000)) {
AccessibilityServiceUtils.goToAccessibilitySetting(App.getApp());
if (!enableAccessibilityServiceByRootAndWaitFor(AccessibilityWatchDogService.class, 3000)) {
goToAccessibilitySetting();
}
} else {
AccessibilityServiceUtils.goToAccessibilitySetting(App.getApp());
goToAccessibilitySetting();
}
}
public static void goToAccessibilitySetting() {
Context context = App.getApp();
if (Pref.isFirstGoToAccessibilitySetting()) {
Toast.makeText(context, context.getString(R.string.text_please_choose) + context.getString(R.string._app_name), Toast.LENGTH_LONG).show();
}
AccessibilityServiceUtils.goToAccessibilitySetting(context);
}
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;
}
}
}
}

View File

@@ -0,0 +1,19 @@
package com.stardust.scriptdroid.tool;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import com.stardust.scriptdroid.App;
/**
* Created by Stardust on 2017/3/10.
*/
public class ClipboardTool {
public static void setClip(CharSequence text) {
((ClipboardManager) App.getApp().getSystemService(Context.CLIPBOARD_SERVICE)).setPrimaryClip(ClipData.newPlainText("", text));
}
}

View File

@@ -3,14 +3,9 @@ package com.stardust.scriptdroid.tool;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import com.afollestad.materialdialogs.folderselector.FileChooserDialog;
import com.stardust.app.OnActivityResultDelegate;
import com.stardust.scriptdroid.R;

View File

@@ -1,27 +1,17 @@
package com.stardust.scriptdroid.tool;
import android.app.Activity;
import android.content.res.AssetFileDescriptor;
import android.os.ParcelFileDescriptor;
import android.preference.PreferenceManager;
import android.util.Log;
import com.stardust.scriptdroid.App;
import com.stericson.RootShell.RootShell;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import jackpal.androidterm.ShellTermSession;
import jackpal.androidterm.Term;
import jackpal.androidterm.emulatorview.TermSession;
import jackpal.androidterm.util.TermSettings;
/**

View File

@@ -1,7 +1,11 @@
package com.stardust.scriptdroid.tool;
import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
import android.support.annotation.IdRes;
import android.view.View;
import android.view.Window;
/**
* Created by Stardust on 2017/1/24.
@@ -13,4 +17,13 @@ public class ViewTool {
public static <V extends View> V $(View view, @IdRes int resId) {
return (V) view.findViewById(resId);
}
public static int getStatusBarHeight(Context context) {
int result = 0;
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = context.getResources().getDimensionPixelSize(resourceId);
}
return result;
}
}

View File

@@ -4,7 +4,6 @@ import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;

View File

@@ -9,8 +9,8 @@ import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import com.jraska.console.Console;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.jraska.console.Console;
import com.stardust.scriptdroid.R;
/**

View File

@@ -15,6 +15,15 @@ import android.view.View;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.ui.edit.editor920.Editor920Activity;
import com.stardust.scriptdroid.ui.edit.sidemenu.EditSideMenuFragment;
import com.stardust.scriptdroid.ui.edit.sidemenu.FunctionListRecyclerView;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.util.SparseArrayEntries;
import com.stardust.view.ViewBinding;
import com.stardust.widget.ToolbarMenuItem;
import com.jecelyin.editor.v2.common.Command;
import com.jecelyin.editor.v2.common.SaveListener;
import com.jecelyin.editor.v2.core.widget.TextView;
@@ -22,20 +31,10 @@ import com.jecelyin.editor.v2.ui.EditorDelegate;
import com.jecelyin.editor.v2.view.EditorView;
import com.jecelyin.editor.v2.view.menu.MenuDef;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.ui.edit.completion.InputMethodEnhanceBar;
import com.stardust.scriptdroid.ui.edit.editor920.Editor920Activity;
import com.stardust.scriptdroid.ui.edit.sidemenu.AssistClipListRecyclerView;
import com.stardust.scriptdroid.ui.edit.sidemenu.EditSideMenuFragment;
import com.stardust.scriptdroid.ui.edit.sidemenu.FunctionListRecyclerView;
import com.stardust.theme.ThemeColorManager;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.util.SparseArrayEntries;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import com.stardust.widget.ToolbarMenuItem;
import java.io.File;
@@ -154,15 +153,7 @@ public class EditActivity extends Editor920Activity {
insertText(function.name);
mDrawerLayout.closeDrawer(GravityCompat.END);
}
})
.setOnClipClickListener(new AssistClipListRecyclerView.OnClipClickListener() {
@Override
public void onClick(String clip, int position) {
insertText(clip);
mDrawerLayout.closeDrawer(GravityCompat.END);
}
});
}
private void setUpEditor() {

View File

@@ -4,8 +4,8 @@ import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import com.jecelyin.editor.v2.core.widget.TextView;
import com.stardust.scriptdroid.Pref;
import com.jecelyin.editor.v2.core.widget.TextView;
import java.util.ArrayList;
import java.util.Collection;

View File

@@ -1,130 +0,0 @@
package com.stardust.scriptdroid.ui.edit.sidemenu;
import android.bug.WrapContentLinearLayoutManager;
import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistClipList;
import com.stardust.scriptdroid.bounds_assist.SharedPrefBoundsAssistClipList;
import com.stardust.widget.ExpandableRecyclerView;
/**
* Created by Stardust on 2017/2/4.
*/
public class AssistClipListRecyclerView extends ExpandableRecyclerView {
public interface OnClipClickListener {
void onClick(String clip, int position);
}
private BoundsAssistClipList mBoundsAssistClipList = SharedPrefBoundsAssistClipList.getInstance();
private OnClipClickListener mOnClipClickListener;
public AssistClipListRecyclerView(Context context) {
super(context);
init();
}
public AssistClipListRecyclerView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
public AssistClipListRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
setLayoutManager(new WrapContentLinearLayoutManager(getContext()));
setAdapter(new Adapter());
setOnChildClickListener(new OnChildClickListener() {
@Override
public void onClick(View view, int position) {
if (mOnClipClickListener != null) {
String clip = (String) ((ChildViewHolder) getChildViewHolder(view)).mTextView.getText();
mOnClipClickListener.onClick(clip, position);
}
}
});
mBoundsAssistClipList.setOnClipChangedListener(new BoundsAssistClipList.OnClipChangedListener() {
@Override
public void onClipRemove(int position) {
if (isExpanded())
getAdapter().notifyItemRemoved(position);
}
@Override
public void onClipInsert(int position) {
if (isExpanded())
getAdapter().notifyItemInserted(position);
}
@Override
public void onChange() {
getAdapter().notifyDataSetChanged();
}
});
}
public void setOnClipClickListener(OnClipClickListener onClipClickListener) {
mOnClipClickListener = onClipClickListener;
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
//防止因mAssistClipList持有OnClipChangedListener引用从而导致这个RecyclerView及其Context被引用内存泄漏
mBoundsAssistClipList.setOnClipChangedListener(null);
}
private class Adapter extends ExpandableRecyclerView.DefaultTitleAdapter {
Adapter() {
setIcon(R.drawable.ic_robot_head_green);
setTitle(R.string.text_assist_clip);
}
@Override
protected RecyclerView.ViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
return new ChildViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.assist_clip_list_recycler_view_item, parent, false));
}
@Override
protected void onBindChildViewHolder(RecyclerView.ViewHolder holder, int position) {
ChildViewHolder viewHolder = (ChildViewHolder) holder;
viewHolder.mTextView.setText(mBoundsAssistClipList.get(position));
}
@Override
protected int getChildItemCount() {
return mBoundsAssistClipList.size();
}
@Override
protected int getChildItemViewType(int position) {
return 0;
}
}
private class ChildViewHolder extends RecyclerView.ViewHolder {
TextView mTextView;
ChildViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView.findViewById(R.id.clip);
}
}
}

View File

@@ -9,27 +9,23 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.stardust.app.Fragment;
import com.stardust.scriptdroid.external.floating_window.FloatingWindowManger;
import com.stardust.view.ViewBinding;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.ui.help.DocumentationActivity;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.ui.console.ConsoleActivity;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistant;
import com.stardust.scriptdroid.external.notification.bounds_assist.BoundsAssistSwitchNotification;
import com.stardust.scriptdroid.ui.help.HelpCatalogueActivity;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import static com.stardust.scriptdroid.external.notification.bounds_assist.BoundsAssistSwitchNotification.KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE;
/**
* Created by Stardust on 2017/2/4.
*/
public class EditSideMenuFragment extends com.stardust.app.Fragment {
public class EditSideMenuFragment extends Fragment {
private FunctionListRecyclerView.OnFunctionClickListener mOnFunctionClickListener;
private AssistClipListRecyclerView.OnClipClickListener mOnClipClickListener;
public static EditSideMenuFragment setFragment(AppCompatActivity activity, int viewId) {
EditSideMenuFragment fragment = new EditSideMenuFragment();
@@ -37,7 +33,7 @@ public class EditSideMenuFragment extends com.stardust.app.Fragment {
return fragment;
}
private SwitchCompat mAssistServiceSwitch, mAssistServiceNotificationSwitch;
private SwitchCompat mFloatingWindowSwitch;
@Nullable
@Override
@@ -48,7 +44,7 @@ public class EditSideMenuFragment extends com.stardust.app.Fragment {
@Override
public void onStart() {
super.onStart();
if (mAssistServiceSwitch == null) {
if (mFloatingWindowSwitch == null) {
setUpUI();
}
syncSwitchState();
@@ -57,14 +53,9 @@ public class EditSideMenuFragment extends com.stardust.app.Fragment {
private void setUpUI() {
setUpSwitchCompat();
setUpFunctionList();
setUpClipList();
ViewBinder.bind(this);
}
private void setUpClipList() {
AssistClipListRecyclerView assistClipListRecyclerView = $(R.id.assist_clip_list);
assistClipListRecyclerView.setOnClipClickListener(mOnClipClickListener);
}
private void setUpFunctionList() {
FunctionListRecyclerView functionListRecyclerView = $(R.id.function_list);
@@ -73,15 +64,11 @@ public class EditSideMenuFragment extends com.stardust.app.Fragment {
}
private void syncSwitchState() {
mAssistServiceSwitch.setChecked(BoundsAssistant.isAssistModeEnable());
mAssistServiceNotificationSwitch.setChecked(BoundsAssistSwitchNotification.isEnable());
mFloatingWindowSwitch.setChecked(FloatingWindowManger.isFloatingWindowShowing());
}
private void setUpSwitchCompat() {
mAssistServiceSwitch = $(R.id.sw_assist_service);
mAssistServiceNotificationSwitch = $(R.id.sw_assist_service_notification);
App.getStateObserver().register(BoundsAssistant.KEY_BOUNDS_ASSIST_ENABLE, mAssistServiceSwitch);
App.getStateObserver().register(KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE, mAssistServiceNotificationSwitch);
mFloatingWindowSwitch = $(R.id.sw_floating_window);
}
@ViewBinding.Click(R.id.syntax_and_api)
@@ -94,24 +81,18 @@ public class EditSideMenuFragment extends com.stardust.app.Fragment {
startActivity(new Intent(getContext(), ConsoleActivity.class).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
}
@ViewBinding.Check(R.id.sw_assist_service)
private void setAssistServiceEnable(boolean enable) {
BoundsAssistant.setAssistModeEnable(enable);
@ViewBinding.Check(R.id.sw_floating_window)
private void setFloatingWindowEnable(boolean enable) {
if (enable && !FloatingWindowManger.isFloatingWindowShowing()) {
FloatingWindowManger.showFloatingWindow();
} else if (!enable && FloatingWindowManger.isFloatingWindowShowing()) {
FloatingWindowManger.hideFloatingWindow();
}
}
@ViewBinding.Click(R.id.assist_service)
@ViewBinding.Click(R.id.floating_window)
private void toggleAssistServiceSwitch() {
mAssistServiceSwitch.toggle();
}
@ViewBinding.Check(R.id.sw_assist_service_notification)
private void setAssistServiceNotificationEnable(boolean enable) {
BoundsAssistSwitchNotification.setEnable(enable);
}
@ViewBinding.Click(R.id.assist_service_notification)
private void toggleAssistServiceNotificationSwitch() {
mAssistServiceNotificationSwitch.toggle();
mFloatingWindowSwitch.toggle();
}
public EditSideMenuFragment setOnFunctionClickListener(FunctionListRecyclerView.OnFunctionClickListener onFunctionClickListener) {
@@ -119,8 +100,4 @@ public class EditSideMenuFragment extends com.stardust.app.Fragment {
return this;
}
public EditSideMenuFragment setOnClipClickListener(AssistClipListRecyclerView.OnClipClickListener onClipClickListener) {
mOnClipClickListener = onClipClickListener;
return this;
}
}

View File

@@ -14,8 +14,8 @@ import android.widget.Toast;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.R;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.scriptdroid.R;
import java.util.Timer;
import java.util.TimerTask;

View File

@@ -11,7 +11,6 @@ import android.widget.Toast;
import com.heinrichreimersoftware.androidissuereporter.IssueReporterActivity;
import com.heinrichreimersoftware.androidissuereporter.model.github.GithubTarget;
import com.stardust.scriptdroid.R;
import com.stardust.theme.ThemeColorManager;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

View File

@@ -3,13 +3,11 @@ package com.stardust.scriptdroid.ui.help;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.View;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.widget.CommonMarkdownView;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.file.FileUtils;
import com.stardust.widget.CommonMarkdownView;
import java.io.IOException;

View File

@@ -12,12 +12,12 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.file.FileUtils;
import com.stardust.scriptdroid.ui.BaseActivity;
import org.json.JSONObject;

View File

@@ -12,7 +12,6 @@ import android.content.pm.PackageManager;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Environment;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.DrawerLayout;
@@ -29,37 +28,31 @@ import com.afollestad.materialdialogs.MaterialDialog;
import com.afollestad.materialdialogs.folderselector.FileChooserDialog;
import com.stardust.app.NotRemindAgainDialog;
import com.stardust.app.OnActivityResultDelegate;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.BuildConfig;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
import com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchNotification;
import com.stardust.scriptdroid.file.FileUtils;
import com.stardust.scriptdroid.file.SampleFileManager;
import com.stardust.scriptdroid.record.inputevent.InputEventRecorder;
import com.stardust.scriptdroid.record.inputevent.InputEventToJsConverter;
import com.stardust.scriptdroid.record.inputevent.InputEventToJsRecorder;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.scriptdroid.tool.BackPressedHandler;
import com.stardust.scriptdroid.tool.ImageSelector;
import com.stardust.scriptdroid.tool.Shell;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperation;
import com.stardust.scriptdroid.ui.settings.SettingsActivity;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import com.stardust.widget.SlidingUpPanel;
import com.stardust.scriptdroid.BuildConfig;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.file.FileUtils;
import com.stardust.scriptdroid.tool.BackPressedHandler;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperation;
import com.stardust.view.ViewBinder;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import java.io.File;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends BaseActivity implements FileChooserDialog.FileCallback {
@@ -85,7 +78,6 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
checkPermissions();
registerReceivers();
handleIntent(getIntent());
//Shell.test(this);
}
private void registerReceivers() {

View File

@@ -3,6 +3,7 @@ package com.stardust.scriptdroid.ui.main;
import android.content.Context;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.DividerItemDecoration;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -14,17 +15,20 @@ import android.view.ViewGroup;
import android.widget.TextView;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.tool.ViewTool;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperation;
import com.stardust.scriptdroid.ui.main.operation.ScriptFileOperationPopupMenu;
import com.stardust.scriptdroid.tool.ClassTool;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import static com.stardust.scriptdroid.tool.ViewTool.$;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import static com.stardust.scriptdroid.ui.main.operation.ScriptFileOperation.*;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Stardust on 2017/1/23.
@@ -38,16 +42,15 @@ public class ScriptListRecyclerView extends ThemeColorRecyclerView {
@Override
public void onClick(View v) {
int position = getChildViewHolder(v).getAdapterPosition();
new ScriptFileOperation.Run().operate(ScriptListRecyclerView.this, mScriptFileList, position);
onItemClicked(v, position);
}
};
private final OnClickListener mOnEditIconClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
int position = getChildViewHolder((View) v.getParent()).getAdapterPosition();
new ScriptFileOperation.Edit().operate(ScriptListRecyclerView.this, mScriptFileList, position);
onEditIconClick(v, position);
}
};
@@ -55,7 +58,6 @@ public class ScriptListRecyclerView extends ThemeColorRecyclerView {
@Override
public void onClick(View v) {
mOperateFileIndex = getChildViewHolder((View) v.getParent()).getAdapterPosition();
//showOperationDialog(position);
showOrDismissOperationPopupMenu(v);
}
@@ -93,31 +95,40 @@ public class ScriptListRecyclerView extends ThemeColorRecyclerView {
}
private void initScriptFileOperationPopupMenu() {
mScriptFileOperationPopupMenu = new ScriptFileOperationPopupMenu(getContext());
mScriptFileOperationPopupMenu = new ScriptFileOperationPopupMenu(getContext(), getScriptFileOperations());
mScriptFileOperationPopupMenu.setOnItemClickListener(new ScriptFileOperationPopupMenu.OnItemClickListener() {
@Override
public void onClick(View view, int position) {
ScriptFileOperation.getOperation(position).operate(ScriptListRecyclerView.this, mScriptFileList, mOperateFileIndex);
public void onClick(View view, int position, ScriptFileOperation operation) {
operation.operate(ScriptListRecyclerView.this, mScriptFileList, mOperateFileIndex);
mScriptFileOperationPopupMenu.dismiss();
}
});
}
protected List<ScriptFileOperation> getScriptFileOperations() {
List<ScriptFileOperation> scriptFileOperations = new ArrayList<>();
scriptFileOperations.add(new ScriptFileOperation.Run());
scriptFileOperations.add(new ScriptFileOperation.Rename());
scriptFileOperations.add(new ScriptFileOperation.OpenByOtherApp());
scriptFileOperations.add(new ScriptFileOperation.CreateShortcut());
scriptFileOperations.add(new ScriptFileOperation.Remove());
scriptFileOperations.add(new ScriptFileOperation.Delete());
return scriptFileOperations;
}
protected void onItemClicked(View v, int position) {
new ScriptFileOperation.Run().operate(ScriptListRecyclerView.this, mScriptFileList, position);
}
protected void onEditIconClick(View v, int position) {
new ScriptFileOperation.Edit().operate(ScriptListRecyclerView.this, mScriptFileList, position);
}
public void setScriptFileList(ScriptFileList scriptFileList) {
mScriptFileList = scriptFileList;
getAdapter().notifyDataSetChanged();
}
private void showOperationDialog(final int position) {
new ThemeColorMaterialDialogBuilder(getContext()).items(ScriptFileOperation.getOperationNames())
.itemsCallback(new MaterialDialog.ListCallback() {
@Override
public void onSelection(MaterialDialog dialog, View itemView, int operation, CharSequence text) {
ScriptFileOperation.getOperation(operation).operate(ScriptListRecyclerView.this, mScriptFileList, position);
}
}).show();
}
private void showOrDismissOperationPopupMenu(View v) {
if (mScriptFileOperationPopupMenu.isShowing()) {
mScriptFileOperationPopupMenu.dismiss();
@@ -126,6 +137,23 @@ public class ScriptListRecyclerView extends ThemeColorRecyclerView {
}
}
@Subscribe
public void showMessage(ScriptFileOperation.ShowMessageEvent event) {
Snackbar.make(this, event.messageResId, Snackbar.LENGTH_SHORT).show();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
EventBus.getDefault().register(this);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
EventBus.getDefault().unregister(this);
}
private class Adapter extends RecyclerView.Adapter<ViewHolder> {
@Override
@@ -164,18 +192,10 @@ public class ScriptListRecyclerView extends ThemeColorRecyclerView {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
path = (TextView) itemView.findViewById(R.id.path);
$(itemView, R.id.edit).setOnClickListener(mOnEditIconClickListener);
$(itemView, R.id.more).setOnClickListener(mOnMoreIconClickListener);
ViewTool.$(itemView, R.id.edit).setOnClickListener(mOnEditIconClickListener);
ViewTool.$(itemView, R.id.more).setOnClickListener(mOnMoreIconClickListener);
itemView.setOnClickListener(mOnItemClickListener);
}
}
static {
loadScriptFileOperations();
}
private static void loadScriptFileOperations() {
ClassTool.loadClasses(Run.class, Rename.class, OpenByOtherApp.class, CreateShortcut.class, Remove.class, Delete.class);
}
}

View File

@@ -13,18 +13,15 @@ import android.view.ViewGroup;
import com.stardust.app.Fragment;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.bounds_assist.BoundsAssistant;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.external.notification.bounds_assist.BoundsAssistSwitchNotification;
import com.stardust.scriptdroid.external.floating_window.FloatingWindowManger;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.scriptdroid.ui.console.ConsoleActivity;
import com.stardust.scriptdroid.ui.help.HelpCatalogueActivity;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import com.stardust.view.accessibility.AccessibilityServiceUtils;
import static com.stardust.scriptdroid.external.notification.bounds_assist.BoundsAssistSwitchNotification.KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE;
/**
* Created by Stardust on 2017/1/30.
@@ -38,7 +35,7 @@ public class SlideMenuFragment extends Fragment {
activity.getSupportFragmentManager().beginTransaction().replace(viewId, fragment).commit();
}
private SwitchCompat mAutoOperateServiceSwitch, mAssistServiceSwitch, mAssistServiceNotificationSwitch;
private SwitchCompat mAutoOperateServiceSwitch, mAssistServiceSwitch;
@Nullable
@Override
@@ -64,16 +61,12 @@ public class SlideMenuFragment extends Fragment {
mAutoOperateServiceSwitch.setChecked(AccessibilityWatchDogService.isEnable());
}
}, 450);
mAssistServiceSwitch.setChecked(BoundsAssistant.isAssistModeEnable());
mAssistServiceNotificationSwitch.setChecked(BoundsAssistSwitchNotification.isEnable());
mAssistServiceSwitch.setChecked(FloatingWindowManger.isFloatingWindowShowing());
}
private void setUpSwitchCompat() {
mAutoOperateServiceSwitch = $(R.id.sw_auto_operate_service);
mAssistServiceSwitch = $(R.id.sw_assist_service);
mAssistServiceNotificationSwitch = $(R.id.sw_assist_service_notification);
App.getStateObserver().register(BoundsAssistant.KEY_BOUNDS_ASSIST_ENABLE, mAssistServiceSwitch);
App.getStateObserver().register(KEY_BOUNDS_ASSIST_SWITCH_NOTIFICATION_ENABLE, mAssistServiceNotificationSwitch);
mAssistServiceSwitch = $(R.id.sw_floating_window);
}
@@ -101,25 +94,20 @@ public class SlideMenuFragment extends Fragment {
}
}
@ViewBinding.Check(R.id.sw_assist_service)
private void setAssistServiceEnable(boolean enable) {
BoundsAssistant.setAssistModeEnable(enable);
@ViewBinding.Check(R.id.sw_floating_window)
private void setFloatingWindowEnable(boolean enable) {
if (enable && !FloatingWindowManger.isFloatingWindowShowing()) {
FloatingWindowManger.showFloatingWindow();
} else if (!enable && FloatingWindowManger.isFloatingWindowShowing()) {
FloatingWindowManger.hideFloatingWindow();
}
}
@ViewBinding.Click(R.id.assist_service)
@ViewBinding.Click(R.id.floating_window)
private void toggleAssistServiceSwitch() {
mAssistServiceSwitch.toggle();
}
@ViewBinding.Check(R.id.sw_assist_service_notification)
private void setAssistServiceNotificationEnable(boolean enable) {
BoundsAssistSwitchNotification.setEnable(enable);
}
@ViewBinding.Click(R.id.assist_service_notification)
private void toggleAssistServiceNotificationSwitch() {
mAssistServiceNotificationSwitch.toggle();
}
@ViewBinding.Click(R.id.stop_all_running_scripts)
private void stopAllRunningScripts() {

View File

@@ -5,17 +5,20 @@ import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.ui.edit.EditActivity;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.external.shortcut.ShortcutActivity;
import com.stardust.scriptdroid.droid.script.file.ScriptFile;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.external.shortcut.Shortcut;
import com.stardust.scriptdroid.external.shortcut.ShortcutActivity;
import com.stardust.scriptdroid.ui.edit.EditActivity;
import com.stardust.scriptdroid.ui.main.ScriptListRecyclerView;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import org.greenrobot.eventbus.EventBus;
import java.util.ArrayList;
import java.util.List;
@@ -26,27 +29,23 @@ import java.util.List;
public abstract class ScriptFileOperation {
private static List<String> operationNames = new ArrayList<>();
private static List<ScriptFileOperation> operations = new ArrayList<>();
public static class ShowMessageEvent {
public int messageResId;
public static ScriptFileOperation getOperation(int index) {
return operations.get(index);
public ShowMessageEvent(int message) {
this.messageResId = message;
}
}
public static List<String> getOperationNames() {
return operationNames;
}
public abstract void operate(ScriptListRecyclerView recyclerView, ScriptFileList scriptFileList, int position);
private static void addOperation(String name, int iconResId, ScriptFileOperation operation) {
operation.mName = name;
operation.mIconResId = iconResId;
operationNames.add(name);
operations.add(operation);
}
public abstract void operate(RecyclerView recyclerView, ScriptFileList scriptFileList, int position);
private String mName;
public ScriptFileOperation(String name, int iconResId) {
mName = name;
mIconResId = iconResId;
}
private int mIconResId;
public String getName() {
@@ -59,13 +58,13 @@ public abstract class ScriptFileOperation {
public static class Run extends ScriptFileOperation {
static {
addOperation(App.getApp().getString(R.string.text_run), R.drawable.ic_play_green, new Run());
public Run() {
super(App.getApp().getString(R.string.text_run), R.drawable.ic_play_green);
}
@Override
public void operate(ScriptListRecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
Snackbar.make(recyclerView, R.string.text_start_running, Snackbar.LENGTH_SHORT).show();
public void operate(RecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
EventBus.getDefault().post(new ShowMessageEvent(R.string.text_start_running));
ScriptFile scriptFile = scriptFileList.get(position);
scriptFile.run();
}
@@ -73,12 +72,12 @@ public abstract class ScriptFileOperation {
public static class Edit extends ScriptFileOperation {
static {
//addOperation(App.getApp().getString(R.string.text_edit)", R.drawable.ic_edit_green_48dp, new Edit());
public Edit() {
super(App.getApp().getString(R.string.text_edit), R.drawable.ic_edit_green_48dp);
}
@Override
public void operate(ScriptListRecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
public void operate(RecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
Context context = recyclerView.getContext();
ScriptFile scriptFile = scriptFileList.get(position);
EditActivity.editFile(context, scriptFile.name, scriptFile.path);
@@ -87,12 +86,12 @@ public abstract class ScriptFileOperation {
public static class OpenByOtherApp extends ScriptFileOperation {
static {
addOperation(App.getApp().getString(R.string.text_open_by_other_apps), R.drawable.ic_open_in_new_green_48dp, new OpenByOtherApp());
public OpenByOtherApp() {
super(App.getApp().getString(R.string.text_open_by_other_apps), R.drawable.ic_open_in_new_green_48dp);
}
@Override
public void operate(ScriptListRecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
public void operate(RecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
Context context = recyclerView.getContext();
ScriptFile scriptFile = scriptFileList.get(position);
Uri uri = Uri.parse("file://" + scriptFile.path);
@@ -102,12 +101,12 @@ public abstract class ScriptFileOperation {
public static class Rename extends ScriptFileOperation {
static {
addOperation(App.getApp().getString(R.string.text_rename), R.drawable.ic_rename_green, new Rename());
public Rename() {
super(App.getApp().getString(R.string.text_rename), R.drawable.ic_rename_green);
}
@Override
public void operate(final ScriptListRecyclerView recyclerView, final ScriptFileList scriptFileList, final int position) {
public void operate(final RecyclerView recyclerView, final ScriptFileList scriptFileList, final int position) {
String oldName = scriptFileList.get(position).name;
new ThemeColorMaterialDialogBuilder(recyclerView.getContext())
.title(R.string.text_rename)
@@ -125,13 +124,12 @@ public abstract class ScriptFileOperation {
public static class CreateShortcut extends ScriptFileOperation {
static {
addOperation(App.getApp().getString(R.string.text_send_shortcut), R.drawable.ic_shortcut_green, new CreateShortcut());
public CreateShortcut() {
super(App.getApp().getString(R.string.text_send_shortcut), R.drawable.ic_shortcut_green);
}
@Override
public void operate(ScriptListRecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
public void operate(RecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
Context context = recyclerView.getContext();
ScriptFile scriptFile = scriptFileList.get(position);
new Shortcut(context).name(scriptFile.name)
@@ -139,18 +137,18 @@ public abstract class ScriptFileOperation {
.icon(R.drawable.ic_robot_green)
.extras(new Intent().putExtra("path", scriptFile.path))
.send();
Snackbar.make(recyclerView, R.string.text_already_create, Snackbar.LENGTH_SHORT).show();
EventBus.getDefault().post(R.string.text_already_create);
}
}
public static class Remove extends ScriptFileOperation {
static {
addOperation(App.getApp().getString(R.string.text_delete), R.drawable.ic_delete_green_48dp, new Remove());
public Remove() {
super(App.getApp().getString(R.string.text_delete), R.drawable.ic_delete_green_48dp);
}
@Override
public void operate(ScriptListRecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
public void operate(RecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
scriptFileList.remove(position);
recyclerView.getAdapter().notifyItemRemoved(position);
}
@@ -158,14 +156,14 @@ public abstract class ScriptFileOperation {
public static class Delete extends ScriptFileOperation {
static {
addOperation(App.getApp().getString(R.string.text_delete_absolutly), R.drawable.ic_delete_forever_green_48dp, new Delete());
public Delete() {
super(App.getApp().getString(R.string.text_delete_absolutly), R.drawable.ic_delete_forever_green_48dp);
}
@Override
public void operate(ScriptListRecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
public void operate(RecyclerView recyclerView, ScriptFileList scriptFileList, int position) {
boolean succeed = scriptFileList.deleteFromFileSystem(position);
Snackbar.make(recyclerView, succeed ? R.string.text_already_delete : R.string.text_delete_failed, Snackbar.LENGTH_SHORT).show();
EventBus.getDefault().post(new ShowMessageEvent(succeed ? R.string.text_already_delete : R.string.text_delete_failed));
recyclerView.getAdapter().notifyItemRemoved(position);
}
}

View File

@@ -18,9 +18,10 @@ import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.TextView;
import com.stardust.scriptdroid.tool.ViewTool;
import com.stardust.scriptdroid.R;
import static com.stardust.scriptdroid.tool.ViewTool.$;
import java.util.List;
/**
* Created by Stardust on 2017/1/24.
@@ -30,26 +31,31 @@ public class ScriptFileOperationPopupMenu extends PopupWindow {
public interface OnItemClickListener {
void onClick(View view, int position);
void onClick(View view, int position, ScriptFileOperation operation);
}
private Context mContext;
private ScriptFileOperationListRecyclerView mOperationListRecyclerView;
private OnItemClickListener mOnItemClickListener;
private List<ScriptFileOperation> mScriptFileOperationList;
private final View.OnClickListener mOnItemClickRealListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mOnItemClickListener != null) {
int position = mOperationListRecyclerView.getChildViewHolder(v).getAdapterPosition();
mOnItemClickListener.onClick(v, position);
mOnItemClickListener.onClick(v, position, mScriptFileOperationList.get(position));
}
}
};
public ScriptFileOperationPopupMenu(Context context) {
super();
public ScriptFileOperationPopupMenu(Context context, List<ScriptFileOperation> scriptFileOperationList) {
super(context);
mContext = context;
mScriptFileOperationList = scriptFileOperationList;
init();
}
@@ -77,7 +83,7 @@ public class ScriptFileOperationPopupMenu extends PopupWindow {
private float getScreenHeight() {
DisplayMetrics displaymetrics = new DisplayMetrics();
((Activity) mContext).getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getMetrics(displaymetrics);
return displaymetrics.heightPixels;
}
@@ -93,8 +99,9 @@ public class ScriptFileOperationPopupMenu extends PopupWindow {
private void initContentView() {
View contentView = View.inflate(mContext, R.layout.script_file_operation_popup_menu_content, null);
setContentView(contentView);
mOperationListRecyclerView = $(contentView, R.id.operation_list);
mOperationListRecyclerView = ViewTool.$(contentView, R.id.operation_list);
mOperationListRecyclerView.setOnItemClickListener(mOnItemClickRealListener);
mOperationListRecyclerView.setScriptFileOperationList(mScriptFileOperationList);
}
@@ -102,6 +109,8 @@ public class ScriptFileOperationPopupMenu extends PopupWindow {
private OnClickListener mOnItemClickListener;
private List<ScriptFileOperation> mScriptFileOperationList;
public ScriptFileOperationListRecyclerView(Context context) {
super(context);
init();
@@ -117,6 +126,10 @@ public class ScriptFileOperationPopupMenu extends PopupWindow {
init();
}
public void setScriptFileOperationList(List<ScriptFileOperation> scriptFileOperationList) {
mScriptFileOperationList = scriptFileOperationList;
}
private void init() {
setLayoutManager(new LinearLayoutManager(getContext()));
setAdapter(new Adapter<ViewHolder>() {
@@ -128,14 +141,14 @@ public class ScriptFileOperationPopupMenu extends PopupWindow {
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
ScriptFileOperation operation = ScriptFileOperation.getOperation(position);
ScriptFileOperation operation = mScriptFileOperationList.get(position);
holder.operationName.setText(operation.getName());
holder.icon.setImageResource(operation.getIconResId());
}
@Override
public int getItemCount() {
return ScriptFileOperation.getOperationNames().size();
return mScriptFileOperationList.size();
}
});
}
@@ -152,8 +165,8 @@ public class ScriptFileOperationPopupMenu extends PopupWindow {
ViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(mOnItemClickListener);
operationName = $(itemView, R.id.name);
icon = $(itemView, R.id.icon);
operationName = ViewTool.$(itemView, R.id.name);
icon = ViewTool.$(itemView, R.id.icon);
}
}
}

View File

@@ -6,20 +6,19 @@ import android.content.ClipboardManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.scriptdroid.tool.IntentTool;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.view.ViewBinding;
import com.stardust.scriptdroid.BuildConfig;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.tool.IntentTool;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import moe.feng.alipay.zerosdk.AlipayZeroSdk;

View File

@@ -10,15 +10,15 @@ import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Toast;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.file.SampleFileManager;
import com.stardust.scriptdroid.tool.IntentTool;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.ui.error.IssueReportActivity;
import com.stardust.util.MapEntries;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.ui.main.MainActivity;
import com.stardust.theme.app.ColorSelectActivity;
import com.stardust.theme.util.ListBuilder;
import com.stardust.util.MapEntries;
import java.util.List;
import java.util.Map;
@@ -26,7 +26,6 @@ import java.util.Map;
import de.psdev.licensesdialog.LicenseResolver;
import de.psdev.licensesdialog.LicensesDialog;
import de.psdev.licensesdialog.licenses.License;
import de.psdev.licensesdialog.licenses.MozillaPublicLicense11;
/**
* Created by Stardust on 2017/2/2.