Release 2.0.1Beta

This commit is contained in:
hyb1996
2017-03-19 16:21:07 +08:00
parent 85affed538
commit dd787236e7
110 changed files with 2428 additions and 4710 deletions

View File

@@ -16,16 +16,16 @@ public interface OnActivityResultDelegate {
void onActivityResult(int requestCode, int resultCode, Intent data);
interface DelegateHost {
Manager getDelegateManger();
Intermediary getDelegateManger();
}
class Manager implements OnActivityResultDelegate {
class Intermediary implements OnActivityResultDelegate {
private SparseArray<OnActivityResultDelegate> specialDelegate = new SparseArray<>();
private SparseArray<OnActivityResultDelegate> mSpecialDelegate = new SparseArray<>();
private List<OnActivityResultDelegate> mDelegates = new ArrayList<>();
public void onActivityResult(int requestCode, int resultCode, Intent data) {
OnActivityResultDelegate delegate = specialDelegate.get(requestCode);
OnActivityResultDelegate delegate = mSpecialDelegate.get(requestCode);
if (delegate != null) {
delegate.onActivityResult(requestCode, resultCode, data);
}
@@ -39,7 +39,14 @@ public interface OnActivityResultDelegate {
}
public void addDelegate(int requestCode, OnActivityResultDelegate delegate) {
specialDelegate.put(requestCode, delegate);
mSpecialDelegate.put(requestCode, delegate);
}
public void removeDelegate(OnActivityResultDelegate delegate) {
if (mDelegates.remove(delegate)) {
// TODO: 2017/3/16 优化
mSpecialDelegate.removeAt(mSpecialDelegate.indexOfValue(delegate));
}
}
}

View File

@@ -183,7 +183,6 @@ public class HoverMenuView extends RelativeLayout {
MagnetPositioner magnetPositioner = new MagnetPositioner(getResources().getDisplayMetrics(), mSidePullerPositioner, new MagnetPositioner.OnCompletionListener() {
@Override
public void onPullToSideCompleted() {
// TODO: this collapsed logic is duplicated
mVisualState = COLLAPSED;
enableDragging();
mTransitionListener.onCollapsed();
@@ -257,7 +256,6 @@ public class HoverMenuView extends RelativeLayout {
removeAllTabs();
addAllTabs();
// TODO: need null check in case selected tab is gone.
Log.d(TAG, "Content change. Active id: " + mActiveTabId);
Log.d(TAG, "Active tab view: " + findViewById(mActiveTabId.hashCode()));
mActiveTab = findViewById(mActiveTabId.hashCode());
@@ -270,7 +268,7 @@ public class HoverMenuView extends RelativeLayout {
super(context);
mWindowDragWatcher = windowDragWatcher;
mMenuAnchor = new CollapsedMenuAnchor(getResources().getDisplayMetrics(), 10);
mMenuAnchor.setAnchorAt((int) savedAnchor.x, savedAnchor.y); // TODO: savedAnchor isn't really a position, its a tuple of values (side, y-pos)
mMenuAnchor.setAnchorAt((int) savedAnchor.x, savedAnchor.y);
mNavigator = null == navigator ? new DefaultNavigator(context) : navigator;
init();
}
@@ -335,7 +333,6 @@ public class HoverMenuView extends RelativeLayout {
mExitRequestListener = null == exitRequestListener ? new NoOpHoverMenuExitRequestListener() : exitRequestListener;
}
// TODO: create custom object to hold anchor state
public PointF getAnchorState() {
return new PointF(mMenuAnchor.getAnchorSide(), mMenuAnchor.getAnchorNormalizedY());
}
@@ -493,7 +490,6 @@ public class HoverMenuView extends RelativeLayout {
Log.d(TAG, "Enabling dragging - x: " + anchorPosition.x + ", y: " + anchorPosition.y);
Log.d(TAG, "Drag width: " + mTabSizeInPx + ", height: " + mTabSizeInPx);
// TODO: should create a method specifically for placing the drag bounds.
mWindowDragWatcher.deactivate();
mWindowDragWatcher.activate(mDragListener, new Rect(
anchorPosition.x,
@@ -548,7 +544,6 @@ public class HoverMenuView extends RelativeLayout {
// Select the first tab.
setActiveTab(mTabIds.get(0));
// TODO: we might set an adapter while expanded - track down the need for this and change it.
// We force a collapse because at this point we're in a weird initial state.
doCollapse(true);
}
@@ -605,7 +600,6 @@ public class HoverMenuView extends RelativeLayout {
return;
}
// TODO: how can we avoid this null check?
if (null != mActiveTab) {
Log.d(TAG, "Adjusting tab position due to layout change.");
getCollapsedMenuViewController().onDragTo(new Point(

View File

@@ -2,30 +2,27 @@ 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.stardust.automator.AccessibilityEventCommandHost;
import com.stardust.scriptdroid.accessibility.AccessibilityInfoProvider;
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
import com.stardust.scriptdroid.droid.script.JavaScriptEngine;
import com.stardust.scriptdroid.droid.script.RhinoJavaScriptEngine;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
import com.stardust.scriptdroid.layout_inspector.LayoutInspector;
import com.stardust.scriptdroid.record.AccessibilityRecorderDelegate;
import com.stardust.scriptdroid.record.accessibility.AccessibilityActionRecorder;
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.ThemeColor;
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.
*/
@@ -34,12 +31,12 @@ public class App extends Application {
private static final String TAG = "App";
private static WeakReference<App> instance;
private static App instance;
private static StateObserver stateObserver;
private static WeakReference<Activity> currentActivity;
private static Activity currentActivity;
public static App getApp() {
return instance.get();
return instance;
}
public static StateObserver getStateObserver() {
@@ -49,26 +46,40 @@ public class App extends Application {
public void onCreate() {
super.onCreate();
ThemeColorManager.setDefaultThemeColor(new ThemeColor(getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorPrimaryDark), getResources().getColor(R.color.colorAccent)));
ThemeColorManager.init(this);
setUpDebugEnvironment();
init();
configApp();
registerActivityLifecycleCallback();
initAccessibilityServiceDelegates();
}
private void setUpDebugEnvironment() {
if (LeakCanary.isInAnalyzerProcess(this)) {
return;
}
LeakCanary.install(this);
if (!BuildConfig.DEBUG)
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler(ErrorReportActivity.class));
instance = new WeakReference<>(this);
}
private void init() {
ThemeColorManager.setDefaultThemeColor(new ThemeColor(getResources().getColor(R.color.colorPrimary), getResources().getColor(R.color.colorPrimaryDark), getResources().getColor(R.color.colorAccent)));
ThemeColorManager.init(this);
instance = this;
stateObserver = new StateObserver(PreferenceManager.getDefaultSharedPreferences(this));
registerActivityLifecycleCallback();
initAccessibilityServiceDelegates();
}
private void configApp() {
ScriptFileList.setImpl(SharedPrefScriptFileList.getInstance());
JavaScriptEngine.setDefault(new RhinoJavaScriptEngine(DroidRuntime.getRuntime()));
}
private void initAccessibilityServiceDelegates() {
AccessibilityWatchDogService.addDelegateIfNeeded(100, ActionPerformAccessibilityDelegate.class);
AccessibilityWatchDogService.addDelegateIfNeeded(200, AccessibilityRecorderDelegate.getInstance());
AccessibilityWatchDogService.addDelegateIfNeeded(300, AccessibilityEventCommandHost.instance);
AccessibilityWatchDogService.addDelegateIfNeeded(400, AccessibilityInfoProvider.instance);
AccessibilityWatchDogService.addDelegateIfNeeded(200, AccessibilityActionRecorder.getInstance());
AccessibilityWatchDogService.addDelegateIfNeeded(300, AccessibilityEventCommandHost.getInstance());
AccessibilityWatchDogService.addDelegateIfNeeded(400, AccessibilityInfoProvider.getInstance());
AccessibilityWatchDogService.addDelegateIfNeeded(500, LayoutInspector.getInstance());
}
@@ -77,7 +88,7 @@ public class App extends Application {
registerActivityLifecycleCallbacks(new SimpleActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
currentActivity = new WeakReference<>(activity);
currentActivity = activity;
}
@@ -88,14 +99,19 @@ public class App extends Application {
@Override
public void onActivityResumed(Activity activity) {
currentActivity = new WeakReference<>(activity);
currentActivity = activity;
}
@Override
public void onActivityStopped(Activity activity) {
currentActivity = null;
}
});
}
public static Activity currentActivity() {
return currentActivity.get();
return currentActivity;
}
public static String getResString(int id) {

View File

@@ -0,0 +1,33 @@
package com.stardust.scriptdroid.accessibility;
import android.util.SparseArray;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.stardust.scriptdroid.R;
import com.stardust.util.SparseArrayEntries;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_CLICKED;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_LONG_CLICKED;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SCROLL_FORWARD;
/**
* Created by Stardust on 2017/3/16.
*/
public class AccessibilityEventHelper {
private static final SparseArray<Integer> EVENT_ACTION_RES_ID_MAP = new SparseArrayEntries<Integer>()
.entry(TYPE_VIEW_CLICKED, R.string.text_clicked)
.entry(TYPE_VIEW_LONG_CLICKED, R.string.text_long_clicked)
.entry(TYPE_VIEW_SCROLLED, R.string.text_scrolled)
.entry(TYPE_VIEW_TEXT_CHANGED, R.string.text_text_changed)
.sparseArray();
public static int getEventTypeNameResId(AccessibilityEvent event) {
return EVENT_ACTION_RES_ID_MAP.get(event.getEventType());
}
}

View File

@@ -15,11 +15,15 @@ import com.stardust.view.accessibility.AccessibilityDelegate;
public class AccessibilityInfoProvider implements AccessibilityDelegate {
public static AccessibilityInfoProvider instance = new AccessibilityInfoProvider();
private static AccessibilityInfoProvider instance = new AccessibilityInfoProvider();
private String mLatestPackage = "";
private String mLatestActivity = "";
public static AccessibilityInfoProvider getInstance() {
return instance;
}
public synchronized String getLatestPackage() {
return mLatestPackage;
}

View File

@@ -1,6 +1,7 @@
package com.stardust.scriptdroid.droid;
import android.support.annotation.NonNull;
import android.util.Log;
import com.jraska.console.timber.ConsoleTree;
import com.stardust.scriptdroid.App;
@@ -15,6 +16,9 @@ import com.stardust.scriptdroid.tool.FileUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.Serializable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import timber.log.Timber;
@@ -26,12 +30,6 @@ public class Droid {
public static final java.lang.String STAY = "\"stay\";";
static {
Timber.plant(new ConsoleTree.Builder()
.infoColor(0xcc000000)
.build());
}
public static final String UI = "\"ui\";";
public interface OnRunFinishedListener extends Serializable {
@@ -54,13 +52,13 @@ public class Droid {
}
private static final DroidRuntime RUNTIME = DroidRuntime.getRuntime();
public static final JavaScriptEngine JAVA_SCRIPT_ENGINE = new RhinoJavaScriptEngine(RUNTIME);
public static final JavaScriptEngine JAVA_SCRIPT_ENGINE = JavaScriptEngine.getDefault();
private static final OnRunFinishedListener DEFAULT_LISTENER = new SimpleOnRunFinishedListener() {
@Override
public void onException(@NonNull Exception e) {
if (!(e instanceof ScriptStopException)) {
if (!causedByInterruptedException(e)) {
RUNTIME.toast(App.getApp().getString(R.string.text_error) + ": " + e.getMessage());
Timber.e(e, App.getApp().getString(R.string.text_error));
Timber.e(e.getMessage());
}
}
};
@@ -73,12 +71,22 @@ public class Droid {
return instance;
}
public static boolean causedByInterruptedException(Throwable e) {
while (e != null) {
if (e instanceof InterruptedException) {
return true;
}
e = e.getCause();
}
return false;
}
public void runScriptFile(File file) {
runScriptFile(file, null);
}
public void runScriptFile(File file, OnRunFinishedListener listener) {
Timber.v("开始运行脚本文件: " + file);
Timber.v(DateFormat.getTimeInstance().format(new Date()) + "/" + App.getResString(R.string.text_start_running) + file);
listener = listener == null ? DEFAULT_LISTENER : listener;
try {
checkFile(file);

View File

@@ -6,15 +6,23 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Environment;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.util.Log;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
import com.jraska.console.timber.ConsoleTree;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.accessibility.AccessibilityInfoProvider;
import com.stardust.scriptdroid.droid.runtime.action.ActionTarget;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
@@ -43,6 +51,18 @@ import timber.log.Timber;
public class DroidRuntime {
static {
Timber.plant(new ConsoleTree.Builder()
.minPriority(Log.VERBOSE)
.verboseColor(0xff909090)
.debugColor(0xffc88b48)
.infoColor(0xffc9c9c9)
.warnColor(0xffa97db6)
.errorColor(0xffff534e)
.assertColor(0xffff5540)
.build());
}
private static final String TAG = "DroidRuntime";
private static DroidRuntime runtime = new DroidRuntime();
@@ -154,8 +174,11 @@ public class DroidRuntime {
return performAction(target.createAction(AccessibilityNodeInfo.ACTION_PASTE));
}
public void log(@Nullable Object str) {
Timber.i("" + str);
public void log(@Nullable Object o) {
String str = o + "";
SpannableString spannableString = new SpannableString(str);
spannableString.setSpan(new ForegroundColorSpan(Color.BLACK), 0, str.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
Console.writeLine(spannableString);
}
public void err(@Nullable Object o) {
@@ -184,7 +207,7 @@ public class DroidRuntime {
return (T) action.getResult();
}
private void ensureAccessibilityServiceEnable() {
public void ensureAccessibilityServiceEnable() {
if (AccessibilityWatchDogService.getInstance() == null) {
String errorMessage = null;
if (AccessibilityServiceUtils.isAccessibilityServiceEnabled(App.getApp(), AccessibilityWatchDogService.class)) {
@@ -232,14 +255,14 @@ public class DroidRuntime {
return Shell.execCommand(cmd, root != 0);
}
public String getPackageName() {
public String currentPackage() {
ensureAccessibilityServiceEnable();
return ActionPerformAccessibilityDelegate.getLatestPackage();
return AccessibilityInfoProvider.getInstance().getLatestPackage();
}
public String getActivityName() {
public String currentActivity() {
ensureAccessibilityServiceEnable();
return ActionPerformAccessibilityDelegate.getLatestActivity();
return AccessibilityInfoProvider.getInstance().getLatestActivity();
}
public UiSelector selector() {

View File

@@ -69,8 +69,9 @@ public class UiSelector extends UiGlobalSelector {
private static final String TAG = "UiSelector";
public UiObjectCollection find() {
DroidRuntime.getRuntime().ensureAccessibilityServiceEnable();
FindCommand command = new FindCommand();
AccessibilityEventCommandHost.instance.executeAndWaitForEvent(command);
AccessibilityEventCommandHost.getInstance().executeAndWaitForEvent(command);
return command.result;
}
@@ -85,11 +86,7 @@ public class UiSelector extends UiGlobalSelector {
}
public UiObject findOne() {
UiObjectCollection collection = find();
if (collection == null || collection.size() == 0) {
return null;
}
return new UiObject(collection.get(0).getInfo());
return untilFindOne();
}
@NonNull
@@ -103,7 +100,7 @@ public class UiSelector extends UiGlobalSelector {
addFilter(new DfsFilter() {
@Override
protected boolean isIncluded(AccessibilityNodeInfo nodeInfo) {
String fullId = AccessibilityInfoProvider.instance.getLatestPackage() + ":id/" + id;
String fullId = AccessibilityInfoProvider.getInstance().getLatestPackage() + ":id/" + id;
return fullId.equals(nodeInfo.getViewIdResourceName());
}
});

View File

@@ -23,28 +23,19 @@ public class ActionPerformAccessibilityDelegate implements AccessibilityDelegate
public static final Action NO_ACTION = null;
private static final Object ACTION_LOCK = new Object();
private static Action action;
private static String latestPackage, latestActivity;
public static void setAction(Action action) {
synchronized (ActionPerformAccessibilityDelegate.class) {
synchronized (ACTION_LOCK) {
ActionPerformAccessibilityDelegate.action = action;
}
}
public synchronized static String getLatestPackage() {
return latestPackage;
}
public synchronized static String getLatestActivity() {
return latestActivity;
}
@Override
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
synchronized (ActionPerformAccessibilityDelegate.class) {
setLatestComponent(event.getPackageName(), event.getClassName());
synchronized (ACTION_LOCK) {
if (action == NO_ACTION)
return false;
AccessibilityNodeInfo root = service.getRootInActiveWindow();
@@ -66,26 +57,12 @@ public class ActionPerformAccessibilityDelegate implements AccessibilityDelegate
private void onActionPerformed() {
synchronized (ActionPerformAccessibilityDelegate.class) {
synchronized (ACTION_LOCK) {
action = NO_ACTION;
DroidRuntime.getRuntime().notifyActionPerformed();
}
}
private static void setLatestComponent(CharSequence latestPackage, CharSequence latestClass) {
if (latestPackage == null)
return;
ActionPerformAccessibilityDelegate.latestPackage = latestPackage.toString();
if (latestClass == null)
return;
try {
ComponentName componentName = new ComponentName(latestPackage.toString(), latestClass.toString());
ActivityInfo activityInfo = App.getApp().getPackageManager().getActivityInfo(componentName, 0);
ActionPerformAccessibilityDelegate.latestActivity = activityInfo.name;
} catch (PackageManager.NameNotFoundException ignored) {
}
}
}

View File

@@ -14,6 +14,16 @@ import java.util.Map;
public abstract class JavaScriptEngine {
private static JavaScriptEngine defaultEngine;
public static JavaScriptEngine getDefault() {
return defaultEngine;
}
public static void setDefault(JavaScriptEngine engine) {
defaultEngine = engine;
}
Map<String, Object> mVariableMap = new HashMap<>();
public abstract Object execute(String script);
@@ -26,6 +36,8 @@ public abstract class JavaScriptEngine {
public abstract int stopAll();
public abstract String[] getGlobalFunctions();
static class Init {
private static final String INIT_SCRIPT = readInitScript();

View File

@@ -26,6 +26,7 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
}
private final Set<Thread> mThreads = new HashSet<>();
private String[] mFunctions;
public RhinoJavaScriptEngine(DroidRuntime runtime) {
set("droid", runtime);
@@ -51,6 +52,7 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
private Context createContext() {
Context context = Context.enter();
context.setOptimizationLevel(-1);
context.setLanguageVersion(Context.VERSION_1_7);
context.setInstructionObserverThreshold(10000);
return context;
}
@@ -80,6 +82,24 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
return n;
}
@Override
public String[] getGlobalFunctions() {
if (mFunctions == null)
mFunctions = getGlobalFunctionsInner();
return mFunctions;
}
private String[] getGlobalFunctionsInner() {
Scriptable scriptable = (Scriptable) execute("this");
Object[] ids = scriptable.getIds();
String[] functions = new String[ids.length];
for (int i = 0; i < ids.length; i++) {
functions[i] = ids[i].toString();
}
return functions;
}
@Override
public void removeAndDestroy() {
synchronized (mThreads) {
@@ -87,6 +107,7 @@ public class RhinoJavaScriptEngine extends JavaScriptEngine {
}
}
public static class InterruptibleContextFactory extends ContextFactory {
@Override

View File

@@ -8,6 +8,16 @@ import java.io.File;
public abstract class ScriptFileList {
private static ScriptFileList impl;
public static void setImpl(ScriptFileList list) {
impl = list;
}
public static ScriptFileList getImpl() {
return impl;
}
public abstract void add(ScriptFile scriptFile);
public abstract ScriptFile get(int i);
@@ -24,6 +34,6 @@ public abstract class ScriptFileList {
return file.delete();
}
public abstract boolean containsPath(String path);
}

View File

@@ -33,9 +33,11 @@ public class FloatingWindowManger {
Intent intent;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + packageName));
Uri.parse("package:" + packageName))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
} else {
intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + packageName));
intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.parse("package:" + packageName))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
App.getApp().startActivity(intent);
}

View File

@@ -37,11 +37,9 @@ import io.mattcarroll.hover.NavigatorContent;
public class HoverMenuAdapter implements io.mattcarroll.hover.HoverMenuAdapter {
public static final String ID_MAIN = "intro";
public static final String ID_MAIN = "main";
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;

View File

@@ -7,6 +7,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;
@@ -50,8 +51,10 @@ public class HoverMenuService extends Service {
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";
public static final String MESSAGE_MENU_EXPANDING = "MESSAGE_MENU_EXPANDING";
public static final String MESSAGE_MENU_EXIT = "MESSAGE_MENU_EXIT";
private static WeakReference<HoverMenuService> service = new WeakReference<>(null);
private static HoverMenuService service = null;
private static boolean sIsRunning;
public static void startService(Context context) {
@@ -64,8 +67,8 @@ public class HoverMenuService extends Service {
}
public static void stopService() {
if (isServiceRunning() && service.get() != null) {
service.get().stopSelf();
if (isServiceRunning() && service != null) {
service.stopSelf();
setIsRunning(false);
}
}
@@ -92,6 +95,7 @@ public class HoverMenuService extends Service {
private HoverMenu.OnExitListener mWindowHoverMenuMenuExitListener = new HoverMenu.OnExitListener() {
@Override
public void onExitByUserRequest() {
EventBus.getDefault().post(new MessageEvent(MESSAGE_MENU_EXIT));
savePreferredLocation();
mWindowHoverMenu.hide();
stopSelf();
@@ -101,11 +105,11 @@ public class HoverMenuService extends Service {
@Override
public void onCreate() {
super.onCreate();
if (service.get() != null) {
if (service != null) {
stopSelf();
return;
}
service = new WeakReference<>(this);
service = this;
EventBus.getDefault().register(this);
mPrefs = getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
try {
@@ -127,7 +131,7 @@ public class HoverMenuService extends Service {
}
private void initWindowMenu() {
mWindowHoverMenu = (WindowHoverMenu) new HoverMenuBuilder(this)
mWindowHoverMenu = (WindowHoverMenu) new HoverMenuBuilder(new ContextThemeWrapper(this, R.style.AppTheme))
.displayWithinWindow()
.useAdapter(new HoverMenuAdapter(this))
.restoreVisualState(loadPreferredLocation())
@@ -138,6 +142,7 @@ public class HoverMenuService extends Service {
mWindowHoverMenu.setHoverMenuTransitionListener(new SimpleHoverMenuTransitionListener() {
@Override
public void onExpanding() {
EventBus.getDefault().post(new MessageEvent(MESSAGE_MENU_EXPANDING));
captureCurrentWindow();
}
@@ -166,17 +171,20 @@ public class HoverMenuService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mWindowHoverMenu.show();
if (mWindowHoverMenu != null)
mWindowHoverMenu.show();
return START_STICKY;
}
@Override
public void onDestroy() {
mWindowHoverMenu.hide();
EventBus.getDefault().unregister(this);
if (mWindowHoverMenu != null)
mWindowHoverMenu.hide();
if (EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
setIsRunning(false);
if (service.get() == this)
service.clear();
if (service == this)
service = null;
}

View File

@@ -1,21 +1,28 @@
package com.stardust.scriptdroid.external.floating_window.content;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.accessibility.AccessibilityInfoProvider;
import com.stardust.scriptdroid.droid.Droid;
import com.stardust.scriptdroid.external.floating_window.HoverMenuService;
import com.stardust.scriptdroid.layout_inspector.LayoutInspector;
import com.stardust.scriptdroid.tool.ClipboardTool;
import com.stardust.scriptdroid.ui.main.MainActivity;
import com.stardust.util.MessageEvent;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import java.util.ArrayList;
import java.util.Arrays;
@@ -36,11 +43,19 @@ public class MainMenuNavigatorContent implements NavigatorContent {
private View mView;
@ViewBinding.Id(R.id.current_package)
private TextView mCurrentPackageTextView;
@ViewBinding.Id(R.id.current_activity)
private TextView mCurrentActivityTextView;
private String mCurrentPackage, mCurrentActivity;
private Context mContext;
public MainMenuNavigatorContent(Context context) {
mContext = context;
mView = View.inflate(context, R.layout.floating_window_main_menu, null);
ViewBinder.bind(this);
EventBus.getDefault().register(this);
}
@ViewBinding.Click(R.id.layout_hierarchy)
@@ -70,6 +85,13 @@ public class MainMenuNavigatorContent implements NavigatorContent {
Toast.makeText(mView.getContext(), R.string.text_no_running_script, Toast.LENGTH_SHORT).show();
}
@ViewBinding.Click(R.id.open_launcher)
private void openMainActivity() {
App.getApp().startActivity(new Intent(App.getApp(), MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK));
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU));
}
@NonNull
@Override
public View getView() {
@@ -81,11 +103,40 @@ public class MainMenuNavigatorContent implements NavigatorContent {
}
@SuppressLint("SetTextI18n")
private void syncCurrentInfo() {
mCurrentPackage = AccessibilityInfoProvider.getInstance().getLatestPackage();
mCurrentActivity = AccessibilityInfoProvider.getInstance().getLatestActivity();
mCurrentActivityTextView.setText(mContext.getString(R.string.text_current_activity) + mCurrentActivity);
mCurrentPackageTextView.setText(mContext.getString(R.string.text_current_package) + mCurrentPackage);
}
@ViewBinding.Click(R.id.current_activity)
private void copyCurrentActivity() {
ClipboardTool.setClip(mCurrentActivity);
Toast.makeText(mContext, R.string.text_copied, Toast.LENGTH_SHORT).show();
}
@ViewBinding.Click(R.id.current_package)
private void copyCurrentPackage() {
ClipboardTool.setClip(mCurrentPackage);
Toast.makeText(mContext, R.string.text_copied, Toast.LENGTH_SHORT).show();
}
@Override
public void onHidden() {
}
@Subscribe
public void onMessageEvent(MessageEvent event) {
if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXPANDING)) {
syncCurrentInfo();
} else if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXIT)) {
EventBus.getDefault().unregister(this);
}
}
public View findViewById(int id) {
return mView.findViewById(id);
}

View File

@@ -2,24 +2,61 @@ package com.stardust.scriptdroid.external.floating_window.content;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.SwitchCompat;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.accessibility.AccessibilityEventHelper;
import com.stardust.scriptdroid.external.floating_window.HoverMenuService;
import com.stardust.scriptdroid.record.Recorder;
import com.stardust.scriptdroid.record.accessibility.AccessibilityActionRecorder;
import com.stardust.scriptdroid.record.inputevent.InputEventConverter;
import com.stardust.scriptdroid.record.inputevent.TouchRecorder;
import com.stardust.scriptdroid.ui.main.MainActivity;
import com.stardust.util.MessageEvent;
import com.stardust.view.ViewBinder;
import com.stardust.view.ViewBinding;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import io.mattcarroll.hover.Navigator;
import io.mattcarroll.hover.NavigatorContent;
/**
* Created by Stardust on 2017/3/12.
*/
public class RecordNavigatorContent implements NavigatorContent {
public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStateChangedListener {
private View mView;
@ViewBinding.Id(R.id.sw_recorded_by_root)
private SwitchCompat mRecordedByRootSwitch;
@ViewBinding.Id(R.id.sw_record_toast)
private SwitchCompat mRecordToastSwitch;
@ViewBinding.Id(R.id.img_start_or_pause)
private ImageView mStartOrPauseRecordIcon;
@ViewBinding.Id(R.id.text_start_or_pause)
private TextView mStartOrPauseRecordText;
@ViewBinding.Id(R.id.stop_record)
private View mStopRecord;
private Recorder mRecorder;
private Context mContext;
public RecordNavigatorContent(Context context) {
mView = new View(context);
mContext = context;
mView = View.inflate(context, R.layout.floating_window_record, null);
ViewBinder.bind(this);
}
@NonNull
@@ -30,11 +67,118 @@ public class RecordNavigatorContent implements NavigatorContent {
@Override
public void onShown(@NonNull Navigator navigator) {
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
}
@Override
public void onHidden() {
if (EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().unregister(this);
}
}
@ViewBinding.Click(R.id.sw_root_container)
private void toggleRecordedByRootSwitch() {
mRecordedByRootSwitch.toggle();
}
@ViewBinding.Click(R.id.sw_record_toast_container)
private void toggleRecordToastSwitch() {
mRecordToastSwitch.toggle();
}
@ViewBinding.Click(R.id.start_or_pause)
private void startOrPauseRecord() {
if (mRecorder == null) {
startRecord();
} else if (mRecorder.getState() == Recorder.STATE_PAUSED) {
resumeRecord();
} else {
pauseRecord();
}
}
private void resumeRecord() {
mRecorder.resume();
setState(Recorder.STATE_RECORDING);
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU));
}
private void pauseRecord() {
mRecorder.pause();
setState(Recorder.STATE_PAUSED);
}
private void startRecord() {
mRecorder = mRecordedByRootSwitch.isChecked() ? new TouchRecorder() : AccessibilityActionRecorder.getInstance();
AccessibilityActionRecorder.getInstance().setRecordMessageEnabled(mRecordToastSwitch.isChecked());
mRecorder.setOnStateChangedListener(this);
mRecorder.start();
setState(Recorder.STATE_RECORDING);
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU));
}
private void setState(int state) {
mStopRecord.setVisibility(state == Recorder.STATE_STOPPED ? View.GONE : View.VISIBLE);
mStartOrPauseRecordIcon.setImageResource(state == Recorder.STATE_RECORDING ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_48dp);
//我知道这样写代码会被打 但我懒...
mStartOrPauseRecordText.setText(state == Recorder.STATE_RECORDING ? R.string.text_pause_record :
state == Recorder.STATE_PAUSED ? R.string.text_resume_record : R.string.text_start_record);
}
@ViewBinding.Click(R.id.stop_record)
private void stopRecord() {
mRecorder.stop();
setState(Recorder.STATE_STOPPED);
EventBus.getDefault().post(new MessageEvent(HoverMenuService.MESSAGE_COLLAPSE_MENU));
}
public View findViewById(int id) {
return mView.findViewById(id);
}
@Subscribe
public void onMessageEvent(MessageEvent event) {
if (event.message.equals(HoverMenuService.MESSAGE_MENU_EXPANDING)) {
pauseRecord();
}
}
@Subscribe
public void onAccessibilityActionRecordEvent(AccessibilityActionRecorder.AccessibilityActionRecordEvent event) {
if (mRecordToastSwitch.isChecked()) {
Toast.makeText(mContext, AccessibilityEventHelper.getEventTypeNameResId(event.getAccessibilityEvent()), Toast.LENGTH_SHORT).show();
}
}
@Subscribe
public void onTouchRecorderStateChanged(InputEventConverter.RecordStateChangeEvent event) {
if (!event.isRecording()) {
stopRecord();
}
}
@Override
public void onStart() {
Toast.makeText(mContext, R.string.text_start_record, Toast.LENGTH_SHORT).show();
}
@Override
public void onStop() {
MainActivity.onActionRecordStopped(mContext, mRecorder.getCode());
mRecorder = null;
}
@Override
public void onPause() {
}
@Override
public void onResume() {
}
}

View File

@@ -6,6 +6,7 @@ import android.view.ContextThemeWrapper;
import android.view.View;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.script.file.ScriptFileList;
import com.stardust.scriptdroid.droid.script.file.SharedPrefScriptFileList;
import com.stardust.scriptdroid.external.floating_window.view.FloatingScriptFileListView;
@@ -22,7 +23,7 @@ public class ScritpListNavigatorContent implements NavigatorContent {
public ScritpListNavigatorContent(Context context) {
mFloatingScriptFileListView = new FloatingScriptFileListView(new ContextThemeWrapper(context, R.style.AppTheme));
mFloatingScriptFileListView.setScriptFileList(SharedPrefScriptFileList.getInstance());
mFloatingScriptFileListView.setScriptFileList(ScriptFileList.getImpl());
}
@NonNull

View File

@@ -0,0 +1,171 @@
package com.stardust.scriptdroid.external.notification.record;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.support.v4.app.NotificationCompat;
import android.widget.Toast;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.record.Recorder;
import com.stardust.scriptdroid.record.accessibility.AccessibilityActionRecorder;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.service.VolumeChangeObverseService;
import com.stardust.scriptdroid.ui.main.MainActivity;
/**
* Created by Stardust on 2017/2/14.
*/
public class AccessibilityActionRecordNotification {
private static final String INTENT_ACTION = " com.stardust.scriptdroid.Record";
private static final String EXTRA_ACTION = "action";
private static final int ACTION_STOP = 17771;
private static final int ACTION_START_OR_PAUSE = 17772;
private static final int ACTION_DELETE = 17773;
private static final int ACTION_REDO = 17774;
private static final int NOTIFY_ID = 22236;
private static NotificationCompat.Builder builder;
private static BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
int action = intent.getIntExtra(EXTRA_ACTION, 0);
switch (action) {
case ACTION_STOP:
stopRecord(context);
collapseNotificationBar(context);
break;
case ACTION_START_OR_PAUSE:
startOrPauseRecord(context);
collapseNotificationBar(context);
break;
case ACTION_DELETE:
AccessibilityActionRecordNotification.cancelNotification();
stopRecordIfNeeded(context);
VolumeChangeObverseService.stopServiceIfNeeded();
break;
case ACTION_REDO:
redoRecord(context);
break;
}
}
};
static PendingIntent getStartOrPauseIntent() {
Intent intent = new Intent(INTENT_ACTION)
.putExtra(EXTRA_ACTION, ACTION_START_OR_PAUSE);
return PendingIntent.getBroadcast(App.getApp(), ACTION_START_OR_PAUSE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
static PendingIntent getStopIntent() {
Intent intent = new Intent(INTENT_ACTION)
.putExtra(EXTRA_ACTION, ACTION_STOP);
return PendingIntent.getBroadcast(App.getApp(), ACTION_STOP, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
static PendingIntent getDeleteIntent() {
Intent intent = new Intent(INTENT_ACTION)
.putExtra(EXTRA_ACTION, ACTION_DELETE);
return PendingIntent.getBroadcast(App.getApp(), ACTION_DELETE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
static PendingIntent getRedoIntent() {
Intent intent = new Intent(INTENT_ACTION)
.putExtra(EXTRA_ACTION, ACTION_REDO);
return PendingIntent.getService(App.getApp(), ACTION_REDO, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static void showOrUpdateNotification() {
if (builder == null) {
builder = new NotificationCompat.Builder(App.getApp())
.setAutoCancel(false)
.setSmallIcon(R.drawable.ic_robot_head)
.setDeleteIntent(getDeleteIntent())
.setCustomContentView(AccessibilityActionRecordSwitchView.getInstance());
}
showNotification(builder.build());
VolumeChangeObverseService.startServiceIfNeeded(App.getApp());
}
private static void showNotification(Notification notification) {
NotificationManager notificationManager = (NotificationManager) App.getApp().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFY_ID, notification);
App.getApp().registerReceiver(broadcastReceiver, new IntentFilter(INTENT_ACTION));
}
public static void cancelNotification() {
NotificationManager notificationManager = (NotificationManager) App.getApp().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFY_ID);
App.getApp().unregisterReceiver(broadcastReceiver);
}
private static void collapseNotificationBar(Context context) {
context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
}
private static void stopRecordIfNeeded(Context context) {
int state = AccessibilityActionRecorder.getInstance().getState();
if (state == Recorder.STATE_RECORDING || state == Recorder.STATE_PAUSED) {
stopRecord(context);
}
}
private static void startOrPauseRecord(Context context) {
int state = AccessibilityActionRecorder.getInstance().getState();
if (state == Recorder.STATE_PAUSED) {
resumeRecord(context);
} else if (state == Recorder.STATE_RECORDING) {
pauseRecord(context);
} else {
//state == STOPPED
startRecord(context);
}
}
public static void startRecord(Context context) {
if (AccessibilityWatchDogService.getInstance() == null) {
Toast.makeText(context, R.string.text_need_enable_accessibility_service, Toast.LENGTH_SHORT).show();
return;
}
AccessibilityActionRecorder.getInstance().start();
AccessibilityActionRecordSwitchView.getInstance().setState(Recorder.STATE_RECORDING);
Toast.makeText(context, R.string.text_start_record, Toast.LENGTH_SHORT).show();
}
private static void pauseRecord(Context context) {
AccessibilityActionRecorder.getInstance().pause();
AccessibilityActionRecordSwitchView.getInstance().setState(Recorder.STATE_PAUSED);
}
private static void resumeRecord(Context context) {
AccessibilityActionRecorder.getInstance().resume();
AccessibilityActionRecordSwitchView.getInstance().setState(Recorder.STATE_RECORDING);
}
public static void stopRecord(Context context) {
if (AccessibilityActionRecorder.getInstance().getState() != Recorder.STATE_STOPPED) {
AccessibilityActionRecorder.getInstance().stop();
String script = AccessibilityActionRecorder.getInstance().getCode();
AccessibilityActionRecordSwitchView.getInstance().setState(Recorder.STATE_STOPPED);
MainActivity.onActionRecordStopped(context, script);
} else {
Toast.makeText(App.getApp(), R.string.text_not_recording, Toast.LENGTH_SHORT).show();
}
}
private static void redoRecord(Context context) {
if (AccessibilityActionRecorder.getInstance().getState() != Recorder.STATE_STOPPED) {
AccessibilityActionRecorder.getInstance().stop();
AccessibilityActionRecordSwitchView.getInstance().setState(Recorder.STATE_STOPPED);
}
}
}

View File

@@ -4,53 +4,50 @@ import android.widget.RemoteViews;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.record.Recorder;
/**
* Created by Stardust on 2017/2/14.
*/
public class ActionRecordSwitchView extends RemoteViews {
public class AccessibilityActionRecordSwitchView extends RemoteViews {
private static ActionRecordSwitchView instance;
private static AccessibilityActionRecordSwitchView instance;
public static final int STOPPED = 0;
public static final int PAUSED = 1;
public static final int RECORDING = 2;
public static ActionRecordSwitchView getInstance() {
public static AccessibilityActionRecordSwitchView getInstance() {
if (instance == null) {
instance = new ActionRecordSwitchView();
instance = new AccessibilityActionRecordSwitchView();
}
return instance;
}
private ActionRecordSwitchView() {
private AccessibilityActionRecordSwitchView() {
super(App.getApp().getPackageName(), R.layout.remote_views_record_switch);
setUpOnClick();
}
public void setState(int state) {
switch (state) {
case STOPPED:
case Recorder.STATE_STOPPED:
setImageViewResource(R.id.img_start_or_pause, R.drawable.ic_play_arrow_grey600_48dp);
setTextViewText(R.id.text_start_or_pause, App.getApp().getString(R.string.text_start_record));
break;
case RECORDING:
case Recorder.STATE_RECORDING:
setImageViewResource(R.id.img_start_or_pause, R.drawable.ic_pause_grey600_48dp);
setTextViewText(R.id.text_start_or_pause, App.getApp().getString(R.string.text_pause_record));
break;
case PAUSED:
case Recorder.STATE_PAUSED:
setImageViewResource(R.id.img_start_or_pause, R.drawable.ic_play_arrow_grey600_48dp);
setTextViewText(R.id.text_start_or_pause, App.getApp().getString(R.string.text_resume_record));
break;
}
ActionRecordSwitchNotification.showOrUpdateNotification();
AccessibilityActionRecordNotification.showOrUpdateNotification();
}
private void setUpOnClick() {
setOnClickPendingIntent(R.id.stop, ActionRecordSwitchHandleService.getStopIntent());
setOnClickPendingIntent(R.id.start_or_pause, ActionRecordSwitchHandleService.getStartOrPauseIntent());
setOnClickPendingIntent(R.id.close, ActionRecordSwitchHandleService.getDeleteIntent());
setOnClickPendingIntent(R.id.redo, ActionRecordSwitchHandleService.getRedoIntent());
setOnClickPendingIntent(R.id.stop, AccessibilityActionRecordNotification.getStopIntent());
setOnClickPendingIntent(R.id.start_or_pause, AccessibilityActionRecordNotification.getStartOrPauseIntent());
setOnClickPendingIntent(R.id.close, AccessibilityActionRecordNotification.getDeleteIntent());
setOnClickPendingIntent(R.id.redo, AccessibilityActionRecordNotification.getRedoIntent());
}
}

View File

@@ -1,164 +0,0 @@
package com.stardust.scriptdroid.external.notification.record;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
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.service.AccessibilityWatchDogService;
import com.stardust.scriptdroid.service.VolumeChangeListenService;
import com.stardust.scriptdroid.ui.main.MainActivity;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.PAUSED;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.RECORDING;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.STOPPED;
/**
* Created by Stardust on 2017/2/15.
*/
public class ActionRecordSwitchHandleService extends Service {
private static final String EXTRA_INTENT_VALID = "ActionRecordSwitchHandleService.intentValid";
private static final String EXTRA_ACTION = "action";
private static final int ACTION_STOP = 17771;
private static final int ACTION_START_OR_PAUSE = 17772;
private static final int ACTION_DELETE = 17773;
private static final int ACTION_REDO = 17774;
public static PendingIntent getStartOrPauseIntent() {
Intent intent = new Intent(App.getApp(), ActionRecordSwitchHandleService.class)
.putExtra(EXTRA_INTENT_VALID, true)
.putExtra(EXTRA_ACTION, ACTION_START_OR_PAUSE);
return PendingIntent.getService(App.getApp(), ACTION_START_OR_PAUSE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static PendingIntent getStopIntent() {
Intent intent = new Intent(App.getApp(), ActionRecordSwitchHandleService.class)
.putExtra(EXTRA_INTENT_VALID, true)
.putExtra(EXTRA_ACTION, ACTION_STOP);
return PendingIntent.getService(App.getApp(), ACTION_STOP, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static PendingIntent getDeleteIntent() {
Intent intent = new Intent(App.getApp(), ActionRecordSwitchHandleService.class)
.putExtra(EXTRA_INTENT_VALID, true)
.putExtra(EXTRA_ACTION, ACTION_DELETE);
return PendingIntent.getService(App.getApp(), ACTION_DELETE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public static PendingIntent getRedoIntent() {
Intent intent = new Intent(App.getApp(), ActionRecordSwitchHandleService.class)
.putExtra(EXTRA_INTENT_VALID, true)
.putExtra(EXTRA_ACTION, ACTION_REDO);
return PendingIntent.getService(App.getApp(), ACTION_REDO, 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_STOP:
stopRecord(this);
collapseNotificationBar();
break;
case ACTION_START_OR_PAUSE:
startOrPauseRecord(this);
collapseNotificationBar();
break;
case ACTION_DELETE:
ActionRecordSwitchNotification.cancelNotification();
stopRecordIfNeeded();
VolumeChangeListenService.stopServiceIfNeeded();
break;
case ACTION_REDO:
redoRecord(this);
break;
}
}
private void collapseNotificationBar() {
sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
}
private void stopRecordIfNeeded() {
if (AccessibilityRecorderDelegate.getInstance().getState() != STOPPED) {
stopRecord(this);
}
}
private static void startOrPauseRecord(Context context) {
int state = AccessibilityRecorderDelegate.getInstance().getState();
if (state == PAUSED) {
resumeRecord(context);
} else if (state == RECORDING) {
pauseRecord(context);
} else {
//state == STOPPED
startRecord(context);
}
}
public static void startRecord(Context context) {
if (AccessibilityWatchDogService.getInstance() == null) {
Toast.makeText(context, R.string.text_need_enable_accessibility_service, Toast.LENGTH_SHORT).show();
return;
}
AccessibilityRecorderDelegate.getInstance().startRecord();
ActionRecordSwitchView.getInstance().setState(RECORDING);
Toast.makeText(context, R.string.text_start_record, Toast.LENGTH_SHORT).show();
}
private static void pauseRecord(Context context) {
AccessibilityRecorderDelegate.getInstance().pauseRecord();
ActionRecordSwitchView.getInstance().setState(PAUSED);
}
private static void resumeRecord(Context context) {
AccessibilityRecorderDelegate.getInstance().resumeRecord();
ActionRecordSwitchView.getInstance().setState(RECORDING);
}
public static void stopRecord(Context context) {
if (AccessibilityRecorderDelegate.getInstance().getState() != STOPPED) {
String script = AccessibilityRecorderDelegate.getInstance().stopRecord();
ActionRecordSwitchView.getInstance().setState(STOPPED);
MainActivity.onActionRecordStopped(context, script);
} else {
Toast.makeText(App.getApp(), R.string.text_not_recording, Toast.LENGTH_SHORT).show();
}
}
private static void redoRecord(Context context) {
if (AccessibilityRecorderDelegate.getInstance().getState() != STOPPED) {
AccessibilityRecorderDelegate.getInstance().stopRecord();
ActionRecordSwitchView.getInstance().setState(STOPPED);
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}

View File

@@ -1,43 +0,0 @@
package com.stardust.scriptdroid.external.notification.record;
import android.app.Notification;
import android.app.NotificationManager;
import android.content.Context;
import android.support.v4.app.NotificationCompat;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.service.VolumeChangeListenService;
/**
* Created by Stardust on 2017/2/14.
*/
public class ActionRecordSwitchNotification {
private static final int NOTIFY_ID = 22236;
private static NotificationCompat.Builder builder;
public static void showOrUpdateNotification() {
if (builder == null) {
builder = new NotificationCompat.Builder(App.getApp())
.setAutoCancel(false)
.setSmallIcon(R.drawable.ic_robot_head)
.setDeleteIntent(ActionRecordSwitchHandleService.getDeleteIntent())
.setCustomContentView(ActionRecordSwitchView.getInstance());
}
showNotification(builder.build());
VolumeChangeListenService.startServiceIfNeeded(App.getApp());
}
private static void showNotification(Notification notification) {
NotificationManager notificationManager = (NotificationManager) App.getApp().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFY_ID, notification);
}
public static void cancelNotification() {
NotificationManager notificationManager = (NotificationManager) App.getApp().getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFY_ID);
}
}

View File

@@ -9,7 +9,9 @@ import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
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.tool.FileUtils;
import com.stardust.scriptdroid.ui.BaseActivity;
import com.stardust.scriptdroid.ui.main.MainActivity;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
@@ -31,8 +33,8 @@ public class ImportIntentActivity extends BaseActivity {
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, R.string.edit_and_run_handle_intent_error, Toast.LENGTH_LONG).show();
finish();
}
finish();
}
private void handleIntent() {
@@ -41,19 +43,22 @@ public class ImportIntentActivity extends BaseActivity {
if (!TextUtils.isEmpty(path)) {
new ThemeColorMaterialDialogBuilder(this)
.title(R.string.text_please_input_name)
.input(getString(R.string.text_name), new File(path).getName(), new MaterialDialog.InputCallback() {
.input(getString(R.string.text_name), FileUtils.getNameWithoutExtension(path), new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
SharedPrefScriptFileList.getInstance().add(new ScriptFile(input.toString(), path));
ScriptFileList.getImpl().add(new ScriptFile(input.toString(), path));
startMainActivity();
}
})
.show();
} else {
finish();
}
}
private void startMainActivity() {
startActivity(new Intent(this, MainActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK));
finish();
}
}

View File

@@ -1,95 +0,0 @@
package com.stardust.scriptdroid.record;
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
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;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.STOPPED;
/**
* Created by Stardust on 2017/2/14.
*/
public class AccessibilityRecorderDelegate implements AccessibilityDelegate {
private static final int PRIORITY = 200;
private static final long RECORD_TIME_OUT = 10 * 60 * 1000;
private static AccessibilityRecorderDelegate instance;
private int mState = STOPPED;
public static AccessibilityRecorderDelegate getInstance() {
if (instance == null) {
instance = new AccessibilityRecorderDelegate();
}
return instance;
}
private ActionRecorder mRecorder = new ActionRecorder();
private long mRecordStartMillis;
public void startRecord() {
if (mState != STOPPED) {
throw new IllegalStateException("Recording");
}
mState = RECORDING;
mRecorder = new ActionRecorder();
mRecordStartMillis = System.currentTimeMillis();
}
public String stopRecord() {
if (mState == STOPPED) {
throw new IllegalStateException("Not recording");
}
mState = STOPPED;
String script = mRecorder.getScript();
mRecorder = null;
return script;
}
public void pauseRecord() {
if (mState != RECORDING) {
throw new IllegalStateException("Not recording");
}
mState = PAUSED;
}
public void resumeRecord() {
if (mState != PAUSED) {
throw new IllegalStateException("Not paused");
}
mRecorder.onResume();
mState = RECORDING;
}
public void stopRecordIfNeeded() {
if (mRecorder != null) {
mRecorder = null;
}
}
public int getState() {
return mState;
}
@Override
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
if (mState == RECORDING) {
mRecorder.record(service, event);
checkTimeOut();
}
return false;
}
private void checkTimeOut() {
if (System.currentTimeMillis() - mRecordStartMillis > RECORD_TIME_OUT) {
stopRecord();
}
}
}

View File

@@ -0,0 +1,154 @@
package com.stardust.scriptdroid.record;
/**
* Created by Stardust on 2017/3/16.
*/
public interface Recorder {
interface OnStateChangedListener {
void onStart();
void onStop();
void onPause();
void onResume();
}
OnStateChangedListener NO_OPERATION_LISTENER = new OnStateChangedListener() {
@Override
public void onStart() {
}
@Override
public void onStop() {
}
@Override
public void onPause() {
}
@Override
public void onResume() {
}
};
int STATE_NOT_START = 0;
int STATE_RECORDING = 1;
int STATE_PAUSED = 2;
int STATE_STOPPED = 3;
void start();
void stop();
void pause();
void resume();
String getCode();
int getState();
void setOnStateChangedListener(OnStateChangedListener onStateChangedListener);
abstract class DefaultIMPL implements Recorder {
private OnStateChangedListener mOnStateChangedListener = NO_OPERATION_LISTENER;
private final boolean mSync;
private int mState = STATE_NOT_START;
public DefaultIMPL(boolean syncOfState) {
mSync = syncOfState;
}
public DefaultIMPL() {
this(false);
}
public void start() {
checkState(STATE_NOT_START);
setState(STATE_RECORDING);
startImpl();
mOnStateChangedListener.onStart();
}
private void checkState(int... expectedStates) {
for (int expectedState : expectedStates) {
if (mState == expectedState)
return;
}
throw new IllegalStateException();
}
protected abstract void startImpl();
public void stop() {
checkState(STATE_RECORDING, STATE_PAUSED);
setState(STATE_STOPPED);
stopImpl();
mOnStateChangedListener.onStop();
}
protected abstract void stopImpl();
public void pause() {
checkState(STATE_RECORDING);
setState(STATE_PAUSED);
pauseImpl();
mOnStateChangedListener.onPause();
}
protected synchronized void setState(int state) {
if (mSync) {
synchronized (this) {
mState = state;
}
} else {
mState = state;
}
}
public synchronized int getState() {
if (mSync) {
synchronized (this) {
return mState;
}
} else {
return mState;
}
}
protected void pauseImpl() {
}
public void resume() {
checkState(STATE_PAUSED);
setState(STATE_RECORDING);
resumeImpl();
mOnStateChangedListener.onResume();
}
protected void resumeImpl() {
}
public void setOnStateChangedListener(OnStateChangedListener onStateChangedListener) {
mOnStateChangedListener = onStateChangedListener == null ? NO_OPERATION_LISTENER : onStateChangedListener;
}
}
}

View File

@@ -1,4 +1,4 @@
package com.stardust.scriptdroid.record;
package com.stardust.scriptdroid.record.accessibility;
import android.accessibilityservice.AccessibilityService;
import android.os.Build;
@@ -11,6 +11,8 @@ import com.stardust.scriptdroid.layout_inspector.NodeInfo;
import com.stardust.util.SparseArrayEntries;
import com.stardust.view.accessibility.AccessibilityNodeInfoHelper;
import org.greenrobot.eventbus.EventBus;
import java.util.List;
@@ -18,7 +20,7 @@ import java.util.List;
* Created by Stardust on 2017/2/14.
*/
public class ActionRecorder {
public class AccessibilityActionConverter {
private static final SparseArray<EventToScriptConverter> CONVERTER_MAP = new SparseArrayEntries<EventToScriptConverter>()
.entry(AccessibilityEvent.TYPE_VIEW_CLICKED, new DoUtilSucceedConverter("click"))
@@ -36,15 +38,22 @@ public class ActionRecorder {
private StringBuilder mScript = new StringBuilder();
private boolean mFirstAction = true;
public AccessibilityActionConverter(boolean shouldIgnoreFirstAction) {
mShouldIgnoreFirstAction = shouldIgnoreFirstAction;
}
private boolean mShouldIgnoreFirstAction = false;
public void record(AccessibilityService service, AccessibilityEvent event) {
EventToScriptConverter converter = CONVERTER_MAP.get(event.getEventType());
if (converter != null) {
if (mFirstAction) {
if (mFirstAction && mShouldIgnoreFirstAction) {
mFirstAction = false;
return;
}
converter.onAccessibilityEvent(service, event, mScript);
mScript.append("\n");
EventBus.getDefault().post(new AccessibilityActionRecorder.AccessibilityActionRecordEvent(event));
}
}

View File

@@ -0,0 +1,95 @@
package com.stardust.scriptdroid.record.accessibility;
import android.accessibilityservice.AccessibilityService;
import android.view.accessibility.AccessibilityEvent;
import com.stardust.scriptdroid.record.Recorder;
import com.stardust.view.accessibility.AccessibilityDelegate;
/**
* Created by Stardust on 2017/2/14.
*/
public class AccessibilityActionRecorder extends Recorder.DefaultIMPL implements AccessibilityDelegate {
public static class AccessibilityActionRecordEvent {
private final AccessibilityEvent mAccessibilityEvent;
public AccessibilityActionRecordEvent(AccessibilityEvent event) {
mAccessibilityEvent = event;
}
public AccessibilityEvent getAccessibilityEvent() {
return mAccessibilityEvent;
}
}
private static final long RECORD_TIME_OUT = 10 * 60 * 1000;
private static AccessibilityActionRecorder instance;
private boolean mRecordMessageEnabled;
private boolean mShouldIgnoreFirstAction = false;
public static AccessibilityActionRecorder getInstance() {
if (instance == null) {
instance = new AccessibilityActionRecorder();
}
return instance;
}
private AccessibilityActionConverter mConverter;
private long mRecordStartMillis;
public AccessibilityActionRecorder(){
super(true);
}
@Override
protected void startImpl() {
mConverter = new AccessibilityActionConverter(mShouldIgnoreFirstAction);
mRecordStartMillis = System.currentTimeMillis();
}
@Override
protected void stopImpl() {
setState(STATE_NOT_START);
}
@Override
protected void resumeImpl() {
mConverter.onResume();
}
@Override
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
if (getState() == STATE_RECORDING) {
mConverter.record(service, event);
checkTimeOut();
}
return false;
}
private void checkTimeOut() {
if (System.currentTimeMillis() - mRecordStartMillis > RECORD_TIME_OUT) {
stop();
}
}
@Override
public String getCode() {
return mConverter.getScript();
}
public void setShouldIgnoreFirstAction(boolean shouldIgnoreFirstAction) {
mShouldIgnoreFirstAction = shouldIgnoreFirstAction;
}
public void setRecordMessageEnabled(boolean recordMessageEnabled) {
// TODO: 2017/3/16 添加录制提示
mRecordMessageEnabled = recordMessageEnabled;
}
}

View File

@@ -7,6 +7,8 @@ import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.runtime.DroidRuntime;
import com.stardust.scriptdroid.ui.main.MainActivity;
import org.greenrobot.eventbus.EventBus;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -17,6 +19,21 @@ import java.util.regex.Pattern;
public abstract class InputEventConverter {
public static class RecordStateChangeEvent {
private boolean mIsRecording;
public RecordStateChangeEvent(boolean isRecording) {
this.mIsRecording = isRecording;
}
public boolean isRecording() {
return mIsRecording;
}
}
static class Event {
static final Pattern PATTERN = Pattern.compile("^\\[([^\\]]*)\\]\\s+([^:]*):\\s+([^\\s]*)\\s+([^\\s]*)\\s+([^\\s]*)\\s*$");
@@ -100,11 +117,11 @@ public abstract class InputEventConverter {
}
public void notifyRecordStopped() {
MainActivity.onRootRecordStopped(App.getApp());
EventBus.getDefault().post(new RecordStateChangeEvent(false));
}
public void notifyRecordStarted() {
DroidRuntime.getRuntime().toast(App.getApp().getString(R.string.text_start_record));
EventBus.getDefault().post(new RecordStateChangeEvent(true));
}
}

View File

@@ -3,10 +3,9 @@ package com.stardust.scriptdroid.record.inputevent;
import android.preference.PreferenceManager;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.record.Recorder;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import jackpal.androidterm.ShellTermSession;
import jackpal.androidterm.emulatorview.TermSession;
@@ -16,9 +15,8 @@ import jackpal.androidterm.util.TermSettings;
* Created by Stardust on 2017/3/6.
*/
public abstract class InputEventRecorder {
public abstract class InputEventRecorder extends Recorder.DefaultIMPL {
private Thread mRecordThread;
private TermSession mTermSession;
private String mGetEventCommand;
protected InputEventConverter mInputEventConverter;
@@ -31,78 +29,61 @@ public abstract class InputEventRecorder {
public void listen() {
TermSettings settings = new TermSettings(App.getApp().getResources(), PreferenceManager.getDefaultSharedPreferences(App.getApp()));
try {
mTermSession = new ShellTermSession(settings, "su\r") {
boolean enter = false;
@Override
protected void processInput(byte[] data, int offset, int count) {
String[] lines = new String(data, offset, count).split("\n");
for (String line : lines) {
System.out.println(line);
if (!enter && line.endsWith("data # ")) {
mTermSession.write(mGetEventCommand + "\r");
enter = true;
} else if (enter) {
parseAndRecordEvent(line);
}
}
appendToEmulator(data, offset, count);
}
};
mTermSession = new MyShellTermSession(settings, "su\r");
mTermSession.initializeEmulator(80, 40);
mTermSession.write("su\r");
mRecordThread = new Thread(new Runnable() {
@Override
public void run() {
try {
//readOutput(mTermSession);
} catch (Exception e) {
e.printStackTrace();
}
}
});
mRecordThread.start();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public void start() {
@Override
protected void startImpl() {
mInputEventConverter.start();
}
public void pause() {
@Override
protected void pauseImpl() {
mInputEventConverter.pause();
}
private void readOutput(TermSession shell) {
boolean enter = false;
String line;
try {
BufferedReader succeedReader = new BufferedReader(new InputStreamReader(mTermSession.getTermIn()));
while ((line = succeedReader.readLine()) != null) {
System.out.println(line);
if (!enter && line.endsWith("data # ")) {
mTermSession.write(mGetEventCommand + "\r");
enter = true;
} else if (enter) {
parseAndRecordEvent(line);
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
@Override
protected void resumeImpl() {
mInputEventConverter.start();
}
protected abstract void parseAndRecordEvent(String eventStr);
public abstract String getCode();
public void stop() {
mRecordThread.interrupt();
@Override
protected void stopImpl() {
mTermSession.finish();
mInputEventConverter.stop();
}
protected abstract void parseAndRecordEvent(String eventStr);
private class MyShellTermSession extends ShellTermSession {
private boolean mGettingEvents = false;
public MyShellTermSession(TermSettings settings, String initialCommand) throws IOException {
super(settings, initialCommand);
}
@Override
protected void processInput(byte[] data, int offset, int count) {
String[] lines = new String(data, offset, count).split("\n");
for (String line : lines) {
System.out.println(line);
if (!mGettingEvents && line.endsWith("data # ")) {
mTermSession.write(mGetEventCommand + "\r");
mGettingEvents = true;
} else if (mGettingEvents) {
parseAndRecordEvent(line);
}
}
appendToEmulator(data, offset, count);
}
}
}

View File

@@ -13,7 +13,6 @@ import java.util.Map;
public class InputEventToJsConverter extends InputEventConverter {
interface EventHandler {
void handle(Event event);
}
@@ -22,10 +21,6 @@ public class InputEventToJsConverter extends InputEventConverter {
private Map<String, EventHandler> mEventHandlerMap = new HashMap<>();
EventHandler getHandler(String key) {
return mEventHandlerMap.get(key);
}
Router put(String key, EventHandler handler) {
mEventHandlerMap.put(key, handler);
return this;
@@ -112,6 +107,7 @@ public class InputEventToJsConverter extends InputEventConverter {
.entry("KEY_VOLUMEDOWN", "VolumeDown")
.entry("KEY_VOLUMEUP", "VolumeUp")
.entry("KEY_BACK", "Back")
.entry("KEY_CAMERA", "Camera")
.map();
@Override
@@ -124,10 +120,10 @@ public class InputEventToJsConverter extends InputEventConverter {
mSwipe = false;
}
} else if (event.value.equals("UP")) {
if (!mStarted && event.code.equals("KEY_VOLUMEUP")) {
if (!mStarted && event.code.equals(mStartTriggerKey)) {
mStarted = true;
notifyRecordStarted();
} else if (mStarted && event.code.equals("KEY_VOLUMEDOWN")) {
} else if (mStarted && event.code.equals(mStopTriggerKey)) {
mStarted = false;
notifyRecordStopped();
} else {
@@ -145,6 +141,7 @@ public class InputEventToJsConverter extends InputEventConverter {
}
private EventHandler mEventHandler = new TypeRouter();
private String mStartTriggerKey, mStopTriggerKey;
private StringBuilder mCode = new StringBuilder().append("var sh = new Shell(true);\n");
private Point mTouchPoint = new Point(), mLastTouchPoint = new Point();
private boolean mTouchDown = false, mSwipe = false;
@@ -166,5 +163,13 @@ public class InputEventToJsConverter extends InputEventConverter {
return mCode.toString();
}
public void setStartTriggerKey(String startTriggerKey) {
mStartTriggerKey = startTriggerKey;
}
public void setStopTriggerKey(String stopTriggerKey) {
mStopTriggerKey = stopTriggerKey;
}
}

View File

@@ -6,9 +6,11 @@ package com.stardust.scriptdroid.record.inputevent;
public class InputEventToJsRecorder extends InputEventRecorder {
private InputEventToJsConverter mInputEventToJsConverter;
public InputEventToJsRecorder() {
super("getevent -t -l", new InputEventToJsConverter());
mInputEventToJsConverter = (InputEventToJsConverter) mInputEventConverter;
}
@Override
@@ -20,4 +22,12 @@ public class InputEventToJsRecorder extends InputEventRecorder {
public String getCode() {
return mInputEventConverter.getCode();
}
public void setStartTriggerKey(String startTriggerKey) {
mInputEventToJsConverter.setStartTriggerKey(startTriggerKey);
}
public void setStopTriggerKey(String stopTriggerKey) {
mInputEventToJsConverter.setStopTriggerKey(stopTriggerKey);
}
}

View File

@@ -0,0 +1,26 @@
package com.stardust.scriptdroid.record.inputevent;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.record.Recorder;
/**
* Created by Stardust on 2017/3/16.
*/
public class TouchRecorder extends InputEventToJsRecorder {
public TouchRecorder() {
listen();
setUpTriggers();
}
private void setUpTriggers() {
setStartTriggerKey(Pref.def().getString(App.getResString(R.string.key_start_record_trigger), null));
setStopTriggerKey(Pref.def().getString(App.getResString(R.string.key_stop_record_trigger), null));
}
}

View File

@@ -23,15 +23,6 @@ import java.util.Map;
*/
public class SampleFileManager {
private static final Map<String, Integer> SAMPLES = new MapEntries<>(new LinkedHashMap<String, Integer>())
.entry("sample/自动操作/sample_open_running_services.js", R.string.text_sample_open_running_services)
.entry("sample/自动操作/sample_qq_hongbao.js", R.string.text_sample_qq_hongbao)
.entry("sample/自动操作/sample_simple_calculator.js", R.string.text_sample_simple_calculator)
.entry("sample/自动操作/sample_force_qq_chat.js", R.string.text_sample_for_qq_chat)
.entry("sample/自动操作/sample_wechat_scan.js", R.string.text_sample_wechat_scan)
.entry("sample/自动操作/sample_alipay_scan.js", R.string.text_sample_alipay_scan)
.map();
private static SampleFileManager instance = new SampleFileManager();
public static SampleFileManager getInstance() {
@@ -42,33 +33,6 @@ public class SampleFileManager {
return App.getApp();
}
public void copySampleScriptFileIfNeeded() {
if (!Pref.def().getBoolean(Pref.SAMPLE_SCRIPTS_COPIED, false)) {
copySampleScriptFile();
}
}
public int copySampleScriptFile() {
ScriptFileList list = SharedPrefScriptFileList.getInstance();
int failCount = 0;
for (Map.Entry<String, Integer> entry : SAMPLES.entrySet()) {
String assetFile = entry.getKey();
String name = getContext().getString(entry.getValue());
String path = ScriptFile.DEFAULT_FOLDER + name + ".js";
if (!list.containsPath(path)) {
if (FileUtils.copyAsset(assetFile, path)) {
list.add(new ScriptFile(name, path));
} else {
failCount++;
}
}
}
if (failCount < SAMPLES.size()) {
Pref.def().edit().putBoolean(Pref.SAMPLE_SCRIPTS_COPIED, true).apply();
}
return failCount;
}
public List<SampleGroup> getSamplesFromAssets(AssetManager assets, String path) {
List<SampleGroup> sampleGroups = new ArrayList<>();
try {

View File

@@ -81,7 +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());
//Log.v(TAG, "delegate: " + entry.getValue().getClass().getName());
if (entry.getValue().onAccessibilityEvent(this, event))
break;
}

View File

@@ -8,34 +8,35 @@ import android.content.IntentFilter;
import android.os.IBinder;
import android.support.annotation.Nullable;
import com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchHandleService;
import com.stardust.scriptdroid.record.AccessibilityRecorderDelegate;
import com.stardust.scriptdroid.external.notification.record.AccessibilityActionRecordNotification;
import com.stardust.scriptdroid.record.Recorder;
import com.stardust.scriptdroid.record.accessibility.AccessibilityActionRecorder;
import com.stardust.scriptdroid.Pref;
import java.lang.ref.WeakReference;
import static com.stardust.scriptdroid.external.notification.record.ActionRecordSwitchView.STOPPED;
/**
* Created by Stardust on 2017/2/16.
*/
public class VolumeChangeListenService extends Service {
public class VolumeChangeObverseService extends Service {
private static final String ACTION_VOLUME_CHANGE = "android.media.VOLUME_CHANGED_ACTION";
private VolumeChangeReceiver mVolumeChangeReceiver;
private static WeakReference<VolumeChangeListenService> instance;
private static VolumeChangeObverseService instance;
public static void stopServiceIfNeeded() {
if (instance != null && instance.get() != null) {
instance.get().stopSelf();
if (instance != null) {
instance.stopSelf();
instance = null;
}
}
public static void startServiceIfNeeded(Context context) {
if (Pref.isRecordVolumeControlEnable() && (instance == null || instance.get() == null)) {
context.startService(new Intent(context, VolumeChangeListenService.class));
if (Pref.isRecordVolumeControlEnable() && instance == null) {
context.startService(new Intent(context, VolumeChangeObverseService.class));
}
}
@@ -43,10 +44,10 @@ public class VolumeChangeListenService extends Service {
@Override
public void onCreate() {
super.onCreate();
if (instance != null && instance.get() != null) {
instance.get().stopSelf();
if (instance != null) {
instance.stopSelf();
}
instance = new WeakReference<>(this);
instance = this;
mVolumeChangeReceiver = new VolumeChangeReceiver();
registerReceiver(mVolumeChangeReceiver, new IntentFilter(ACTION_VOLUME_CHANGE));
}
@@ -60,6 +61,7 @@ public class VolumeChangeListenService extends Service {
@Override
public void onDestroy() {
super.onDestroy();
instance = null;
unregisterReceiver(mVolumeChangeReceiver);
}
@@ -74,10 +76,10 @@ public class VolumeChangeListenService extends Service {
return;
}
mLastChangeMillis = System.currentTimeMillis();
if (AccessibilityRecorderDelegate.getInstance().getState() == STOPPED) {
ActionRecordSwitchHandleService.startRecord(VolumeChangeListenService.this);
if (AccessibilityActionRecorder.getInstance().getState() == Recorder.STATE_STOPPED) {
AccessibilityActionRecordNotification.startRecord(VolumeChangeObverseService.this);
} else {
ActionRecordSwitchHandleService.stopRecord(VolumeChangeListenService.this);
AccessibilityActionRecordNotification.stopRecord(VolumeChangeObverseService.this);
}
}
}

View File

@@ -25,7 +25,7 @@ public interface BackPressedHandler {
mActivity = activity;
}
public DoublePressExit setDoublePressInterval(long doublePressInterval) {
public DoublePressExit doublePressInterval(long doublePressInterval) {
mDoublePressInterval = doublePressInterval;
return this;
}

View File

@@ -16,15 +16,15 @@ import com.stardust.scriptdroid.R;
public class ImageSelector implements OnActivityResultDelegate {
public interface ImageSelectorCallback {
void onImageSelected(String path);
void onImageSelected(ImageSelector selector, String path);
}
private static final int REQUEST_CODE = "LOVE EATING".hashCode() >> 16;
private Activity mActivity;
private ImageSelectorCallback mCallback;
public ImageSelector(Activity activity, Manager manager, ImageSelectorCallback callback) {
manager.addDelegate(REQUEST_CODE, this);
public ImageSelector(Activity activity, Intermediary intermediary, ImageSelectorCallback callback) {
intermediary.addDelegate(REQUEST_CODE, this);
mActivity = activity;
mCallback = callback;
}
@@ -48,7 +48,7 @@ public class ImageSelector implements OnActivityResultDelegate {
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
String filePath = cursor.getString(columnIndex);
cursor.close();
mCallback.onImageSelected(filePath);
mCallback.onImageSelected(this, filePath);
}

View File

@@ -38,49 +38,6 @@ public class Shell {
private StringBuilder mSucceedOutput = new StringBuilder();
private StringBuilder mErrorOutput = new StringBuilder();
public static String bytesToString(byte[] data, int base, int length) {
StringBuilder buf = new StringBuilder();
for(int i = 0; i < length; ++i) {
byte b = data[base + i];
if(b >= 32 && b <= 126) {
buf.append((char)b);
} else {
buf.append(String.format("\\x%02x", new Object[]{Byte.valueOf(b)}));
}
}
return buf.toString();
}
public static void test(final Activity activity) {
TermSettings settings = new TermSettings(App.getApp().getResources(), PreferenceManager.getDefaultSharedPreferences(App.getApp()));
try {
final ShellTermSession termSession = new ShellTermSession(settings, "");
termSession.initializeEmulator(80, 40);
termSession.write("su\r");
termSession.write("getevent\r");
new Thread(new Runnable() {
@Override
public void run() {
BufferedReader reader = new BufferedReader(new InputStreamReader(termSession.getTermIn()));
try {
while (reader.ready()){
String line = reader.readLine();
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
} catch (IOException e) {
e.printStackTrace();
}
}
public Shell() {
this(false);
}
@@ -97,7 +54,6 @@ public class Shell {
}
public Shell execute(String command) {
try {
mCommandOutputStream.writeBytes(command);
if (!command.endsWith(COMMAND_LINE_END)) {

View File

@@ -38,6 +38,9 @@ import com.stardust.theme.ThemeColorManager;
import com.stardust.view.ViewBinder;
import java.io.File;
import java.util.Timer;
import timber.log.Timber;
/**
* Created by Stardust on 2017/1/29.
@@ -98,8 +101,10 @@ public class EditActivity extends Editor920Activity {
if (intent.getAction().equals(ACTION_ON_RUN_FINISHED)) {
setMenuStatus(R.id.run, MenuDef.STATUS_NORMAL);
String msg = intent.getStringExtra(EXTRA_EXCEPTION_MESSAGE);
if (msg != null)
if (msg != null) {
Snackbar.make(mView, getString(R.string.text_error) + ": " + msg, Snackbar.LENGTH_LONG).show();
Timber.e(msg);
}
}
}
};

View File

@@ -101,11 +101,16 @@ public class CodeCompletion implements TextWatcher {
private static final String[] KEYWORDS = {"arguments", "break", "case", "catch", "class", "continue", "default", "do", "else", "eval", "export", "false", "for", "function", "if", "import", "in", "int", "new", "null", "package", "return", "switch", "this", "throw", "throws", "true", "try", "typeof", "var", "volatile", "while", "with", "Array", "Date", "hasOwnProperty", "Infinity", "isFinite", "isNaN", "isPrototypeOf", "length", "Math", "NaN", "name", "Number", "Object", "prototype", "String", "toString", "undefined", "valueOf"};
private static final int KEY_WORD_LENGTH_MAX = 15;
private static final String[] FUNCTIONS = {"toast", "launchPackage", "launch", "launchApp", "click", "longClick", "scrollUp", "scrollDown", "select", "focus", "paste", "input", "sleep", "isStopped", "notStopped", "log", "err", "openConsole", "clearConsole", "shell", "getTexts", "getPackageName", "getActivityName", "setClip", "addAccessibilityDelegate"};
public void setFunctions(String[] functions) {
mFunctions = functions;
}
private String[] mFunctions = {"toast", "launchPackage", "launch", "launchApp", "click", "longClick", "scrollUp", "scrollDown", "select", "focus", "paste", "input", "sleep", "isStopped", "notStopped", "log", "err", "openConsole", "clearConsole", "shell", "getTexts", "getPackageName", "getActivityName", "setClip", "addAccessibilityDelegate"};
private boolean searchCodeCompletion(String str) {
Collection<CodeCompletionItem> c = searchWordCompletion(str);
c.addAll(searchCodeCompletion(str, FUNCTIONS));
c.addAll(searchCodeCompletion(str, mFunctions));
c.addAll(searchKeyWordCompletion(str));
if (c.size() > 0) {
mOnCodeCompletionChangeListener.OnCodeCompletionChange(c, DEFAULT_CODE_COMPLETION_LIST);

View File

@@ -14,6 +14,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.droid.script.JavaScriptEngine;
import java.util.ArrayList;
import java.util.Collection;
@@ -73,6 +74,7 @@ public class InputMethodEnhanceBar extends RecyclerView implements CodeCompletio
private void init() {
setAdapter(new CodeCompletionAdapter());
setLayoutManager(new LinearLayoutManager(getContext(), HORIZONTAL, false));
mCodeCompletion.setFunctions(JavaScriptEngine.getDefault().getGlobalFunctions());
}
public void setEditTextBridge(EditTextBridge editTextBridge) {

View File

@@ -11,6 +11,8 @@ import com.jecelyin.editor.v2.ui.IActivity;
import com.jecelyin.editor.v2.ui.TabManager;
import java.io.File;
import java.lang.reflect.Field;
import java.util.Map;
/**
* Created by Stardust on 2017/1/30.
@@ -21,6 +23,18 @@ public class Editor920Activity extends IActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Pref.getInstance(this).setReadOnly(false);
setPref(Pref.KEY_PREF_KEEP_BACKUP_FILE, false);
}
private void setPref(String key, Object value) {
try {
Field field = Pref.class.getDeclaredField("map");
field.setAccessible(true);
((Map) field.get(Pref.getInstance(this))).put(key, value);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
@Override

View File

@@ -66,11 +66,19 @@ public class IssueReportActivity extends IssueReporterActivity {
final String errorDetail = getIntent().getStringExtra("error");
if (errorDetail != null) {
((EditText) findViewById(R.id.air_inputDescription)).setText(errorDetail);
((EditText) findViewById(R.id.air_inputTitle)).setText(R.string.text_crash_en);
String title = getFirstLine(errorDetail);
((EditText) findViewById(R.id.air_inputTitle)).setText(title);
mCrash = true;
}
}
private String getFirstLine(String str) {
int i = str.indexOf('\n');
if(i < 0)
return str;
return str.substring(0, i);
}
private boolean validateInput() {
if (mValidateInput == null) {
try {

View File

@@ -9,12 +9,17 @@ import com.stardust.widget.CommonMarkdownView;
import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.tool.FileUtils;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Created by Stardust on 2017/2/1.
*/
public class DocumentationActivity extends BaseActivity {
private CommonMarkdownView mCommonMarkdownView;

View File

@@ -33,7 +33,7 @@ import com.stardust.scriptdroid.Pref;
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.external.notification.record.AccessibilityActionRecordNotification;
import com.stardust.scriptdroid.record.inputevent.InputEventRecorder;
import com.stardust.scriptdroid.record.inputevent.InputEventToJsRecorder;
import com.stardust.scriptdroid.service.AccessibilityWatchDogService;
@@ -68,12 +68,14 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
private static final String ARGUMENT_SCRIPT = "ARGUMENT_SCRIPT";
private static final String ACTION_ON_ROOT_RECORD_STOPPED = "ACTION_ON_ROOT_RECORD_STOPPED";
private SlidingUpPanel mAddFilePanel;
private ScriptFileList mScriptFileList;
private DrawerLayout mDrawerLayout;
@ViewBinding.Id(R.id.bottom_menu)
private SlidingUpPanel mAddBottomMenuPanel;
private MyScriptListFragment mMyScriptListFragment;
private SampleScriptListFragment mSampleScriptListFragment;
private DrawerLayout mDrawerLayout;
private OnActivityResultDelegate.Manager mManager = new OnActivityResultDelegate.Manager();
private OnActivityResultDelegate.Intermediary mActivityResultIntermediary = new OnActivityResultDelegate.Intermediary();
private BackPressedHandler.Observer mBackPressObserver = new BackPressedHandler.Observer();
@Override
@@ -113,7 +115,7 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
private void addScriptFile(final String path) {
new ThemeColorMaterialDialogBuilder(this).title(R.string.text_name)
.inputType(InputType.TYPE_CLASS_TEXT)
.input(getString(R.string.text_please_input_name), new File(path).getName(), new MaterialDialog.InputCallback() {
.input(getString(R.string.text_please_input_name), FileUtils.getNameWithoutExtension(path), new MaterialDialog.InputCallback() {
@Override
public void onInput(@NonNull MaterialDialog dialog, CharSequence input) {
MainActivity.this.addScriptFile(input.toString(), path);
@@ -130,7 +132,6 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
mDrawerLayout = (DrawerLayout) View.inflate(this, R.layout.activity_main, null);
setContentView(mDrawerLayout);
setUpFragment();
mAddFilePanel = $(R.id.bottom_menu);
setUpToolbar();
setUpTabLayout();
setUpDrawerHeader();
@@ -141,7 +142,7 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
private void setUpFragment() {
SlideMenuFragment.setFragment(this, R.id.fragment_slide_menu);
mMyScriptListFragment = new MyScriptListFragment();
mScriptFileList = SharedPrefScriptFileList.getInstance();
mScriptFileList = ScriptFileList.getImpl();
mSampleScriptListFragment = new SampleScriptListFragment();
}
@@ -188,7 +189,7 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
@ViewBinding.Click(R.id.add)
private void showAddFilePanel() {
mAddFilePanel.show();
mAddBottomMenuPanel.show();
}
@ViewBinding.Click(R.id.create_new_file)
@@ -238,7 +239,7 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
Snackbar.make(mDrawerLayout, R.string.text_need_enable_accessibility_service, Snackbar.LENGTH_SHORT).show();
return;
}
ActionRecordSwitchNotification.showOrUpdateNotification();
AccessibilityActionRecordNotification.showOrUpdateNotification();
Snackbar.make(mDrawerLayout, R.string.hint_start_record, Snackbar.LENGTH_SHORT).show();
}
@@ -265,11 +266,12 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
@ViewBinding.Click(R.id.drawer_header_img)
public void selectHeaderImage() {
new ImageSelector(this, mManager, new ImageSelector.ImageSelectorCallback() {
new ImageSelector(this, mActivityResultIntermediary, new ImageSelector.ImageSelectorCallback() {
@Override
public void onImageSelected(String path) {
public void onImageSelected(ImageSelector selector, String path) {
setDrawerHeaderImage(path);
Pref.def().edit().putString(Pref.KEY_DRAWER_HEADER_IMAGE_PATH, path).apply();
mActivityResultIntermediary.removeDelegate(selector);
}
}).select();
}
@@ -334,8 +336,8 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
@Override
public void onBackPressed() {
if (mAddFilePanel.isShowing()) {
mAddFilePanel.dismiss();
if (mAddBottomMenuPanel.isShowing()) {
mAddBottomMenuPanel.dismiss();
} else if (mDrawerLayout.isDrawerOpen(Gravity.START)) {
mDrawerLayout.closeDrawer(Gravity.START);
} else {
@@ -350,7 +352,7 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
mManager.onActivityResult(requestCode, resultCode, data);
mActivityResultIntermediary.onActivityResult(requestCode, resultCode, data);
}
public static void onActionRecordStopped(Context context, String script) {
@@ -370,11 +372,12 @@ public class MainActivity extends BaseActivity implements FileChooserDialog.File
}
public void OnAppBarClick(View view) {
new ImageSelector(this, mManager, new ImageSelector.ImageSelectorCallback() {
new ImageSelector(this, mActivityResultIntermediary, new ImageSelector.ImageSelectorCallback() {
@Override
public void onImageSelected(String path) {
public void onImageSelected(ImageSelector selector, String path) {
Pref.def().edit().putString(Pref.KEY_APP_BAR_IMAGE_PATH, path).apply();
setAppBarImage(path);
mActivityResultIntermediary.removeDelegate(selector);
}
}).select();
}

View File

@@ -39,8 +39,7 @@ public class MyScriptListFragment extends Fragment implements BackPressedHandler
@Nullable
@Override
public View createView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_my_script_list, container, false);
return view;
return inflater.inflate(R.layout.fragment_my_script_list, container, false);
}
@Override
@@ -55,7 +54,7 @@ public class MyScriptListFragment extends Fragment implements BackPressedHandler
private void setUpScriptList() {
mScriptListRecyclerView = $(R.id.script_list);
mScriptFileList = SharedPrefScriptFileList.getInstance();
mScriptFileList = ScriptFileList.getImpl();
mNoScriptHint = $(R.id.hint_no_script);
mScriptListRecyclerView.getAdapter().registerAdapterDataObserver(new SimpleAdapterDataObserver() {
@Override
@@ -91,6 +90,18 @@ public class MyScriptListFragment extends Fragment implements BackPressedHandler
}
}
@Override
public void onResume() {
super.onResume();
EventBus.getDefault().register(mScriptListRecyclerView);
}
@Override
public void onPause() {
super.onPause();
EventBus.getDefault().unregister(mScriptListRecyclerView);
}
@Override
public void onDestroy() {
super.onDestroy();

View File

@@ -139,18 +139,6 @@ public class ScriptListRecyclerView extends ThemeColorRecyclerView {
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

View File

@@ -2,13 +2,27 @@ package com.stardust.scriptdroid.ui.main.sample_list;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.app.Fragment;
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.sample.Sample;
import com.stardust.scriptdroid.sample.SampleFileManager;
import com.stardust.scriptdroid.tool.FileUtils;
import com.stardust.scriptdroid.ui.edit.EditActivity;
import com.stardust.scriptdroid.ui.main.my_script_list.MyScriptListFragment;
import com.stardust.util.MessageEvent;
import org.greenrobot.eventbus.EventBus;
/**
* Created by Stardust on 2017/3/13.
@@ -28,7 +42,47 @@ public class SampleScriptListFragment extends Fragment {
protected void afterCreateView() {
mSampleScriptListRecyclerView = $(R.id.script_list);
mSampleScriptListRecyclerView.setSamples(SampleFileManager.getInstance().getSamplesFromAssets(getContext().getAssets(), "sample"));
mSampleScriptListRecyclerView.setOnItemLongClickListener(new SampleScriptListRecyclerView.OnItemLongClickListener() {
@Override
public void onItemLongClick(Sample sample) {
showMenuDialog(sample);
}
});
mSampleScriptListRecyclerView.setOnItemClickListener(new SampleScriptListRecyclerView.OnItemClickListener() {
@Override
public void onItemClick(Sample sample) {
viewSample(sample);
}
});
}
private void showMenuDialog(final Sample sample) {
new MaterialDialog.Builder(getActivity())
.title(sample.name)
.items(getString(R.string.text_copy_to_my_scripts))
.itemsCallback(new MaterialDialog.ListCallback() {
@Override
public void onSelection(MaterialDialog dialog, View itemView, int position, CharSequence text) {
copySampleToMyScripts(sample);
}
})
.show();
}
private void copySampleToMyScripts(Sample sample) {
String path = ScriptFile.DEFAULT_FOLDER + sample.name + ".js";
if (!ScriptFileList.getImpl().containsPath(path) && FileUtils.copyAsset(sample.path, path)) {
ScriptFileList.getImpl().add(new ScriptFile(sample.name, path));
EventBus.getDefault().post(new MessageEvent(MyScriptListFragment.MESSAGE_SCRIPT_FILE_ADDED));
Snackbar.make(mSampleScriptListRecyclerView, R.string.text_import_succeed, Snackbar.LENGTH_SHORT).show();
} else {
Snackbar.make(mSampleScriptListRecyclerView, R.string.text_file_exists, Snackbar.LENGTH_SHORT).show();
}
}
private void viewSample(Sample sample) {
EditActivity.editAssetFile(getContext(), sample.name, sample.path);
}
}

View File

@@ -29,14 +29,36 @@ import java.util.List;
public class SampleScriptListRecyclerView extends RecyclerView {
public interface OnItemLongClickListener {
void onItemLongClick(Sample sample);
}
private OnClickListener mOnItemClickListener = new OnClickListener() {
public interface OnItemClickListener {
void onItemClick(Sample sample);
}
private OnClickListener mOnItemClickListenerProxy = new OnClickListener() {
@Override
public void onClick(View v) {
SampleViewHolder viewHolder = (SampleViewHolder) getChildViewHolder(v);
viewSample(viewHolder.getChild());
if (mOnItemClickListener != null) {
SampleViewHolder viewHolder = (SampleViewHolder) getChildViewHolder(v);
mOnItemClickListener.onItemClick(viewHolder.getChild());
}
}
};
private OnLongClickListener mOnLongClickListenerProxy = new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
if (mOnItemLongClickListener != null) {
SampleViewHolder viewHolder = (SampleViewHolder) getChildViewHolder(v);
mOnItemLongClickListener.onItemLongClick(viewHolder.getChild());
return true;
}
return false;
}
};
private OnItemLongClickListener mOnItemLongClickListener;
private OnItemClickListener mOnItemClickListener;
private Adapter mAdapter;
@@ -57,6 +79,14 @@ public class SampleScriptListRecyclerView extends RecyclerView {
init();
}
public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) {
mOnItemLongClickListener = onItemLongClickListener;
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
mOnItemClickListener = onItemClickListener;
}
public void setSamples(List<com.stardust.scriptdroid.sample.SampleGroup> samples) {
mSampleGroups.clear();
for (com.stardust.scriptdroid.sample.SampleGroup sampleGroup : samples) {
@@ -71,12 +101,6 @@ public class SampleScriptListRecyclerView extends RecyclerView {
addItemDecoration(new DividerItemDecoration(getContext(), VERTICAL));
}
private void viewSample(Sample sample) {
EditActivity.editAssetFile(getContext(), sample.name, sample.path);
}
private class SampleGroup implements Parent<Sample> {
private com.stardust.scriptdroid.sample.SampleGroup mSampleGroup;
@@ -108,7 +132,8 @@ public class SampleScriptListRecyclerView extends RecyclerView {
super(itemView);
name = (TextView) itemView.findViewById(R.id.name);
((LevelBeamView) itemView.findViewById(R.id.level)).setLevel(2);
itemView.setOnClickListener(mOnItemClickListener);
itemView.setOnClickListener(mOnItemClickListenerProxy);
itemView.setOnLongClickListener(mOnLongClickListenerProxy);
}
void bind(Sample sample) {

View File

@@ -3,11 +3,14 @@ package com.stardust.view;
import android.view.View;
import android.widget.CompoundButton;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* Created by Stardust on 2017/1/30.
*
* 哈你说我为什么不用AA框架之前还不知道嘛所以现在用了。
*/
public class ViewBinder {
@@ -21,6 +24,7 @@ public class ViewBinder {
throw new RuntimeException("You must implement findViewById to use view binding", e);
}
Method[] methods = o.getClass().getDeclaredMethods();
bindId(o, findViewById);
for (Method method : methods) {
method.setAccessible(true);
bindClick(o, method, findViewById);
@@ -28,6 +32,20 @@ public class ViewBinder {
}
}
private static void bindId(Object o, Method findViewById) {
for (Field field : o.getClass().getDeclaredFields()) {
field.setAccessible(true);
ViewBinding.Id id = field.getAnnotation(ViewBinding.Id.class);
if (id == null || id.value() == 0)
continue;
try {
field.set(o, findViewById.invoke(o, id.value()));
} catch (IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
private static void bindCheck(final Object o, final Method method, Method findViewById) {
ViewBinding.Check annotation = method.getAnnotation(ViewBinding.Check.class);
if (annotation == null || annotation.value() == 0)

View File

@@ -32,4 +32,10 @@ public @interface ViewBinding {
@interface Check {
int value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface Id {
int value();
}
}