diff --git a/app/src/main/java/com/stardust/scriptdroid/App.java b/app/src/main/java/com/stardust/scriptdroid/App.java index a58f0e21..f0903126 100644 --- a/app/src/main/java/com/stardust/scriptdroid/App.java +++ b/app/src/main/java/com/stardust/scriptdroid/App.java @@ -11,6 +11,7 @@ import com.squareup.leakcanary.LeakCanary; import com.stardust.app.SimpleActivityLifecycleCallbacks; import com.stardust.app.VolumeChangeObserver; import com.stardust.scriptdroid.autojs.AutoJs; +import com.stardust.scriptdroid.autojs.record.GlobalRecorder; import com.stardust.scriptdroid.statics.ScriptStatics; import com.stardust.scriptdroid.tool.CrashHandler; import com.stardust.scriptdroid.tool.JsBeautifierFactory; @@ -69,6 +70,7 @@ public class App extends MultiDexApplication { AutoJs.initInstance(this); JsBeautifierFactory.initJsBeautify(this, "js/jsbeautify.js"); initVolumeChangeObserver(); + GlobalRecorder.initSingleton(this); } private void initVolumeChangeObserver() { diff --git a/app/src/main/java/com/stardust/scriptdroid/Pref.java b/app/src/main/java/com/stardust/scriptdroid/Pref.java index 3e7049c7..ef53bbef 100644 --- a/app/src/main/java/com/stardust/scriptdroid/Pref.java +++ b/app/src/main/java/com/stardust/scriptdroid/Pref.java @@ -59,21 +59,6 @@ public class Pref { return def().getBoolean(getString(R.string.key_use_volume_control_running), false); } - public static String getStartRecordTrigger() { - return def().getString(getString(R.string.key_start_record_trigger), null); - } - - public static String getStopRecordTrigger() { - return def().getString(getString(R.string.key_stop_record_trigger), null); - } - - public static boolean hasRecordTrigger() { - String startTrigger = getStartRecordTrigger(); - String stopTrigger = getStartRecordTrigger(); - return startTrigger != null && !startTrigger.equals("NONE") - && stopTrigger != null && !startTrigger.equals("NONE"); - } - public static boolean enableAccessibilityServiceByRoot() { return def().getBoolean(getString(R.string.key_enable_accessibility_service_by_root), false); } @@ -135,4 +120,8 @@ public class Pref { public static boolean isFirstShowingAd() { return getDisposableBoolean(KEY_FIRST_SHOW_AD, true); } + + public static boolean isRecordWithRootEnabled() { + return def().getBoolean(getString(R.string.key_record_with_root), false); + } } diff --git a/app/src/main/java/com/stardust/scriptdroid/autojs/record/GlobalRecorder.java b/app/src/main/java/com/stardust/scriptdroid/autojs/record/GlobalRecorder.java index d7c53501..8ab6d4d5 100644 --- a/app/src/main/java/com/stardust/scriptdroid/autojs/record/GlobalRecorder.java +++ b/app/src/main/java/com/stardust/scriptdroid/autojs/record/GlobalRecorder.java @@ -1,28 +1,229 @@ package com.stardust.scriptdroid.autojs.record; +import android.content.Context; +import android.os.Looper; +import android.support.annotation.NonNull; +import android.view.ContextThemeWrapper; +import android.view.KeyEvent; +import android.view.View; +import android.widget.Toast; + +import com.afollestad.materialdialogs.DialogAction; +import com.afollestad.materialdialogs.MaterialDialog; +import com.stardust.app.DialogUtils; +import com.stardust.autojs.core.inputevent.InputEventCodes; +import com.stardust.autojs.core.inputevent.ShellKeyObserver; import com.stardust.autojs.core.record.Recorder; +import com.stardust.autojs.core.record.inputevent.TouchRecorder; +import com.stardust.autojs.runtime.api.Shell; +import com.stardust.scriptdroid.App; +import com.stardust.scriptdroid.Pref; +import com.stardust.scriptdroid.R; +import com.stardust.scriptdroid.autojs.AutoJs; +import com.stardust.scriptdroid.ui.common.ScriptOperations; +import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; +import com.stardust.util.ClipboardUtil; +import com.stardust.view.accessibility.AccessibilityService; +import com.stardust.view.accessibility.OnKeyListener; + +import java.util.concurrent.CopyOnWriteArrayList; /** * Created by Stardust on 2017/8/6. */ -public class GlobalRecorder extends Recorder.AbstractRecorder { +public class GlobalRecorder implements Recorder.OnStateChangedListener { + private static GlobalRecorder sSingleton; private Recorder mRecorder; + private CopyOnWriteArrayList mOnStateChangedListeners = new CopyOnWriteArrayList<>(); + private TouchRecorder mTouchRecorder; + private Context mContext; + private boolean mDiscard = false; + private long mLastVolumeDownEventTime; + private ShellKeyObserver mShellKeyObserver; - @Override - protected void startImpl() { - + public static GlobalRecorder getSingleton(Context context) { + if (sSingleton == null) { + sSingleton = new GlobalRecorder(context); + } + return sSingleton; } - @Override - protected void stopImpl() { + public static void initSingleton(Context context) { + getSingleton(context); + } + + public GlobalRecorder(Context context) { + mContext = new ContextThemeWrapper(context.getApplicationContext(), R.style.AppTheme); + mTouchRecorder = new TouchRecorder(context); + addKeyListeners(); + } + + private void addKeyListeners() { + AccessibilityService.getStickOnKeyObserver().addListener(new OnKeyListener() { + @Override + public void onKeyEvent(int keyCode, KeyEvent event) { + if (event.getAction() == KeyEvent.ACTION_DOWN && + (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) { + onVolumeDown(); + } + } + + }); + mShellKeyObserver = new ShellKeyObserver(); + mShellKeyObserver.setKeyListener(new ShellKeyObserver.KeyListener() { + @Override + public void onKeyDown(String keyName) { + if ("KEY_VOLUMEDOWN".equals(keyName)) { + onVolumeDown(); + } + } + + @Override + public void onKeyUp(String keyName) { + + } + }); + } + + + private void onVolumeDown() { + if (System.currentTimeMillis() - mLastVolumeDownEventTime < 300) { + return; + } + mLastVolumeDownEventTime = System.currentTimeMillis(); + int state = getState(); + if (state == Recorder.STATE_RECORDING || state == Recorder.STATE_PAUSED) { + stop(); + } else { + start(); + } + } + + public void start() { + if (Pref.isRecordWithRootEnabled()) { + mTouchRecorder.reset(); + mRecorder = mTouchRecorder; + } else { + mRecorder = AutoJs.getInstance().getAccessibilityActionRecorder(); + } + mDiscard = false; + mRecorder.setOnStateChangedListener(this); + mRecorder.start(); + } + + public void pause() { + mRecorder.pause(); + } + + public void resume() { + mRecorder.resume(); + } + + public void stop() { + mRecorder.stop(); } - @Override public String getCode() { - return null; + return mRecorder.getCode(); + } + + public int getState() { + if (mRecorder == null) + return Recorder.STATE_NOT_START; + return mRecorder.getState(); + } + + + public void addOnStateChangedListener(Recorder.OnStateChangedListener listener) { + mOnStateChangedListeners.add(listener); + } + + public boolean removeOnStateChangedListener(Recorder.OnStateChangedListener listener) { + return mOnStateChangedListeners.remove(listener); + } + + @Override + public void onStart() { + App.getApp().getUiHandler().toast(R.string.text_start_record); + for (Recorder.OnStateChangedListener listener : mOnStateChangedListeners) { + listener.onStart(); + } + } + + @Override + public void onStop() { + if (!mDiscard) { + handleRecordedScript(getCode()); + } + for (Recorder.OnStateChangedListener listener : mOnStateChangedListeners) { + listener.onStop(); + } + } + + @Override + public void onPause() { + for (Recorder.OnStateChangedListener listener : mOnStateChangedListeners) { + listener.onPause(); + } + } + + @Override + public void onResume() { + for (Recorder.OnStateChangedListener listener : mOnStateChangedListeners) { + listener.onResume(); + } + } + + public void discard() { + mDiscard = true; + stop(); + } + + private void handleRecordedScript(final String script) { + if (Looper.myLooper() == Looper.getMainLooper()) { + showRecordHandleDialog(script); + } else { + App.getApp().getUiHandler().post(new Runnable() { + @Override + public void run() { + showRecordHandleDialog(script); + } + }); + } + } + + private void showRecordHandleDialog(final String script) { + DialogUtils.showDialog(new ThemeColorMaterialDialogBuilder(mContext) + .title(R.string.text_recorded) + .items(getString(R.string.text_new_file), getString(R.string.text_copy_to_clip)) + .itemsCallback(new MaterialDialog.ListCallback() { + @Override + public void onSelection(MaterialDialog dialog, View itemView, int position, CharSequence text) { + if (position == 0) { + new ScriptOperations(mContext, null) + .newScriptFileForScript(script); + } else { + ClipboardUtil.setClip(mContext, script); + Toast.makeText(mContext, R.string.text_already_copy_to_clip, Toast.LENGTH_SHORT).show(); + } + } + }) + .negativeText(R.string.text_cancel) + .onNegative(new MaterialDialog.SingleButtonCallback() { + @Override + public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { + dialog.dismiss(); + } + }) + .canceledOnTouchOutside(false) + .build()); + } + + private String getString(int res) { + return mContext.getString(res); } } diff --git a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/RecordNavigatorContent.java b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/RecordNavigatorContent.java index eec1ce51..f6c8589d 100644 --- a/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/RecordNavigatorContent.java +++ b/app/src/main/java/com/stardust/scriptdroid/external/floatingwindow/menu/content/RecordNavigatorContent.java @@ -24,6 +24,7 @@ import com.stardust.scriptdroid.Pref; import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.accessibility.AccessibilityEventHelper; import com.stardust.scriptdroid.autojs.AutoJs; +import com.stardust.scriptdroid.autojs.record.GlobalRecorder; import com.stardust.scriptdroid.external.floatingwindow.menu.HoverMenuService; import com.stardust.scriptdroid.ui.common.ScriptOperations; import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; @@ -31,6 +32,8 @@ import com.stardust.util.ClipboardUtil; import com.stardust.util.MessageEvent; import com.stardust.view.accessibility.AccessibilityService; import com.stardust.view.accessibility.OnKeyListener; +import com.stardust.widget.PrefSwitch; +import com.stardust.widget.ViewSwitcher; import org.greenrobot.eventbus.Subscribe; @@ -45,61 +48,36 @@ import io.mattcarroll.hover.NavigatorContent; * Created by Stardust on 2017/3/12. */ -public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStateChangedListener, ShellKeyObserver.KeyListener { +public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStateChangedListener { private View mView; @BindView(R.id.sw_recorded_by_root) - SwitchCompat mRecordedByRootSwitch; + PrefSwitch mRecordedByRootSwitch; @BindView(R.id.sw_record_toast) SwitchCompat mRecordToastSwitch; - @BindView(R.id.img_start_or_pause) - ImageView mStartOrPauseRecordIcon; + @BindView(R.id.img_pause_or_resume) + ImageView mPauseOrResumeImage; - @BindView(R.id.text_start_or_pause) - TextView mStartOrPauseRecordText; + @BindView(R.id.text_pause_or_resume) + TextView mPauseOrResumeText; - @BindView(R.id.stop_record) - View mStopRecord; + @BindView(R.id.view_switcher) + ViewSwitcher mViewSwitcher; - @BindView(R.id.discard_record) - View mDiscardRecord; - - - private Recorder mRecorder; - private TouchRecorder mTouchRecorder; + private GlobalRecorder mRecorder; private Context mContext; - private boolean mDiscard = false; - private ShellKeyObserver mKeyObserver; - private InputEventObserver mInputEventObserver = InputEventObserver.getGlobal(); - private OnKeyListener mVolumeKeyListener = new OnKeyListener() { - @Override - public void onKeyEvent(int keyCode, KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_DOWN && - (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) - && Pref.isRecordVolumeControlEnable()) { - if (mRecorder == null) { - startRecord(); - } else if (alreadyStartRecord()) { - stopRecord(); - } - } - } - }; + public RecordNavigatorContent(Context context) { mContext = new ContextThemeWrapper(context, R.style.AppTheme); mView = View.inflate(context, R.layout.floating_window_record, null); ButterKnife.bind(this, mView); HoverMenuService.getEventBus().register(this); - AccessibilityService.getStickOnKeyObserver().addListener(mVolumeKeyListener); - mTouchRecorder = TouchRecorder.getGlobal(context); - if (Pref.hasRecordTrigger()) { - mKeyObserver = new ShellKeyObserver(); - mInputEventObserver.addListener(mKeyObserver); - mKeyObserver.setKeyListener(this); - } + mRecorder = GlobalRecorder.getSingleton(context); + mRecorder.addOnStateChangedListener(this); + setState(mRecorder.getState()); } @NonNull @@ -128,57 +106,38 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat mRecordToastSwitch.toggle(); } - @OnClick(R.id.start_or_pause) - void startOrPauseRecord() { - if (mRecorder == null) { - startRecord(); - } else if (mRecorder.getState() == Recorder.STATE_PAUSED) { - resumeRecord(); - } else { - pauseRecord(); - } + @OnClick(R.id.start_record) + void startRecord() { + mRecorder.start(); + HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU)); } @OnClick(R.id.discard_record) void discardRecord() { - mDiscard = true; - stopRecord(); + mRecorder.discard(); } - private void resumeRecord() { - mRecorder.resume(); - setState(Recorder.STATE_RECORDING); - HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU)); - } - private void pauseRecord() { - mRecorder.pause(); - setState(Recorder.STATE_PAUSED); - } - - private void startRecord() { - mDiscard = false; - if (mRecordedByRootSwitch.isChecked()) { - mTouchRecorder.reset(); - mRecorder = mTouchRecorder; + @OnClick(R.id.pause_or_resume_record) + void pauseOrResumeRecord() { + if (mRecorder.getState() == Recorder.STATE_PAUSED) { + mRecorder.resume(); } else { - mRecorder = AutoJs.getInstance().getAccessibilityActionRecorder(); + mRecorder.pause(); } - mRecorder.setOnStateChangedListener(this); - mRecorder.start(); - setState(Recorder.STATE_RECORDING); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU)); } private void setState(int state) { - mStopRecord.setVisibility(state == Recorder.STATE_STOPPED ? View.GONE : View.VISIBLE); - mDiscardRecord.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); - + if (state == Recorder.STATE_NOT_START || state == Recorder.STATE_STOPPED) { + mViewSwitcher.showFirst(); + } else { + mViewSwitcher.showSecond(); + } + mPauseOrResumeImage.setImageResource(state == Recorder.STATE_RECORDING ? R.drawable.ic_pause_white_24dp : + R.drawable.ic_play_arrow_white_48dp); + mPauseOrResumeText.setText( + state == Recorder.STATE_RECORDING ? R.string.text_pause_record : R.string.text_resume_record); } @OnClick(R.id.stop_record) @@ -192,8 +151,8 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat @Subscribe public void onMessageEvent(MessageEvent event) { if (event.message.equals(HoverMenuService.ACTION_MENU_EXPANDING)) { - if (mRecorder != null && mRecorder.getState() == Recorder.STATE_RECORDING) - pauseRecord(); + if (mRecorder.getState() == Recorder.STATE_RECORDING) + mRecorder.pause(); } else if (event.message.equals(HoverMenuService.ACTION_MENU_EXIT)) { onMenuExit(); } @@ -201,9 +160,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat public void onMenuExit() { HoverMenuService.getEventBus().unregister(this); - AccessibilityService.getStickOnKeyObserver().addListener(mVolumeKeyListener); - mInputEventObserver.recycle(); - mInputEventObserver.removeListener(mKeyObserver); + mRecorder.removeOnStateChangedListener(this); } @Subscribe @@ -215,80 +172,21 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat @Override public void onStart() { - App.getApp().getUiHandler().toast(R.string.text_start_record); + setState(Recorder.STATE_RECORDING); } @Override public void onStop() { - if (!mDiscard) { - if (mRecorder instanceof TouchRecorder) { - new ScriptOperations(mContext, null) - .importFile(mRecorder.getCode()) - .subscribe(); - } else { - handleRecordedScript(mRecorder.getCode()); - } - } - mRecorder = null; - } - - private void handleRecordedScript(final String script) { - DialogUtils.showDialog(new ThemeColorMaterialDialogBuilder(mContext) - .title(R.string.text_recorded) - .items(getString(R.string.text_new_file), getString(R.string.text_copy_to_clip)) - .itemsCallback(new MaterialDialog.ListCallback() { - @Override - public void onSelection(MaterialDialog dialog, View itemView, int position, CharSequence text) { - if (position == 0) { - new ScriptOperations(mContext, null) - .newScriptFileForScript(script); - } else { - ClipboardUtil.setClip(mContext, script); - Toast.makeText(mContext, R.string.text_already_copy_to_clip, Toast.LENGTH_SHORT).show(); - } - } - }) - .negativeText(R.string.text_cancel) - .onNegative(new MaterialDialog.SingleButtonCallback() { - @Override - public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { - dialog.dismiss(); - } - }) - .canceledOnTouchOutside(false) - .build()); - } - - private String getString(int res) { - return mContext.getString(res); + setState(Recorder.STATE_STOPPED); } @Override public void onPause() { + setState(Recorder.STATE_PAUSED); } @Override public void onResume() { - + setState(Recorder.STATE_RECORDING); } - - @Override - public void onKeyDown(String keyName) { - if (keyName.equals(Pref.getStopRecordTrigger())) { - if (alreadyStartRecord()) - stopRecord(); - } else if (keyName.equals(Pref.getStartRecordTrigger())) { - if (mRecorder == null) - startRecord(); - } - } - - private boolean alreadyStartRecord() { - return mRecorder != null && mRecorder.getState() == Recorder.STATE_RECORDING && mRecorder.getState() == Recorder.STATE_PAUSED; - } - - @Override - public void onKeyUp(String keyName) { - - } -} +} \ No newline at end of file diff --git a/app/src/main/res/drawable/floating_menu_divider.xml b/app/src/main/res/drawable/floating_menu_divider.xml new file mode 100644 index 00000000..0fb65a8b --- /dev/null +++ b/app/src/main/res/drawable/floating_menu_divider.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/floating_window_record.xml b/app/src/main/res/layout/floating_window_record.xml index 5f4156b1..ee22d262 100644 --- a/app/src/main/res/layout/floating_window_record.xml +++ b/app/src/main/res/layout/floating_window_record.xml @@ -11,7 +11,9 @@ + android:divider="@drawable/floating_menu_divider" + android:orientation="vertical" + android:showDividers="middle"> - - - - - - + android:measureAllChildren="false"> - + - + - + - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/floating_window_record_pause.xml b/app/src/main/res/layout/floating_window_record_pause.xml new file mode 100644 index 00000000..4583d64e --- /dev/null +++ b/app/src/main/res/layout/floating_window_record_pause.xml @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/floating_window_record_start.xml b/app/src/main/res/layout/floating_window_record_start.xml new file mode 100644 index 00000000..33321750 --- /dev/null +++ b/app/src/main/res/layout/floating_window_record_start.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + diff --git a/autojs/src/main/java/com/stardust/autojs/core/inputevent/TouchObserver.java b/autojs/src/main/java/com/stardust/autojs/core/inputevent/TouchObserver.java index 6df0949c..a7bd64b3 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/inputevent/TouchObserver.java +++ b/autojs/src/main/java/com/stardust/autojs/core/inputevent/TouchObserver.java @@ -4,6 +4,7 @@ import android.support.annotation.NonNull; import static com.stardust.autojs.core.record.inputevent.InputEventRecorder.parseDeviceNumber; + /** * Created by Stardust on 2017/7/20. */ diff --git a/autojs/src/main/java/com/stardust/autojs/core/record/inputevent/InputEventRecorder.java b/autojs/src/main/java/com/stardust/autojs/core/record/inputevent/InputEventRecorder.java index cbcb9887..e94c3d06 100644 --- a/autojs/src/main/java/com/stardust/autojs/core/record/inputevent/InputEventRecorder.java +++ b/autojs/src/main/java/com/stardust/autojs/core/record/inputevent/InputEventRecorder.java @@ -38,7 +38,7 @@ public abstract class InputEventRecorder extends Recorder.AbstractRecorder imple public abstract String getCode(); - protected static int parseDeviceNumber(String device) { + public static int parseDeviceNumber(String device) { Matcher matcher = LAST_INT_PATTERN.matcher(device); if (matcher.find()) { String someNumberStr = matcher.group(1); diff --git a/common/src/main/java/com/stardust/event/EventDispatcher.java b/common/src/main/java/com/stardust/event/EventDispatcher.java new file mode 100644 index 00000000..8caea547 --- /dev/null +++ b/common/src/main/java/com/stardust/event/EventDispatcher.java @@ -0,0 +1,32 @@ +package com.stardust.event; + +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Created by Stardust on 2017/8/6. + */ + +public class EventDispatcher { + + public interface Event { + void notify(Listener l); + } + + private CopyOnWriteArrayList mListeners = new CopyOnWriteArrayList<>(); + + public void addListener(Listener l) { + mListeners.add(l); + } + + + public boolean removeListener(Listener l) { + return mListeners.remove(l); + } + + public void dispatchEvent(Event event) { + for (Listener listener : mListeners) { + event.notify(listener); + } + } + +}