fix: record isn't stopped when volume down

refactor: recorder
This commit is contained in:
hyb1996
2017-08-12 14:14:49 +08:00
parent 5ee99b5379
commit 4129f83cb1
11 changed files with 434 additions and 271 deletions

View File

@@ -11,6 +11,7 @@ import com.squareup.leakcanary.LeakCanary;
import com.stardust.app.SimpleActivityLifecycleCallbacks; import com.stardust.app.SimpleActivityLifecycleCallbacks;
import com.stardust.app.VolumeChangeObserver; import com.stardust.app.VolumeChangeObserver;
import com.stardust.scriptdroid.autojs.AutoJs; import com.stardust.scriptdroid.autojs.AutoJs;
import com.stardust.scriptdroid.autojs.record.GlobalRecorder;
import com.stardust.scriptdroid.statics.ScriptStatics; import com.stardust.scriptdroid.statics.ScriptStatics;
import com.stardust.scriptdroid.tool.CrashHandler; import com.stardust.scriptdroid.tool.CrashHandler;
import com.stardust.scriptdroid.tool.JsBeautifierFactory; import com.stardust.scriptdroid.tool.JsBeautifierFactory;
@@ -69,6 +70,7 @@ public class App extends MultiDexApplication {
AutoJs.initInstance(this); AutoJs.initInstance(this);
JsBeautifierFactory.initJsBeautify(this, "js/jsbeautify.js"); JsBeautifierFactory.initJsBeautify(this, "js/jsbeautify.js");
initVolumeChangeObserver(); initVolumeChangeObserver();
GlobalRecorder.initSingleton(this);
} }
private void initVolumeChangeObserver() { private void initVolumeChangeObserver() {

View File

@@ -59,21 +59,6 @@ public class Pref {
return def().getBoolean(getString(R.string.key_use_volume_control_running), false); 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() { public static boolean enableAccessibilityServiceByRoot() {
return def().getBoolean(getString(R.string.key_enable_accessibility_service_by_root), false); return def().getBoolean(getString(R.string.key_enable_accessibility_service_by_root), false);
} }
@@ -135,4 +120,8 @@ public class Pref {
public static boolean isFirstShowingAd() { public static boolean isFirstShowingAd() {
return getDisposableBoolean(KEY_FIRST_SHOW_AD, true); return getDisposableBoolean(KEY_FIRST_SHOW_AD, true);
} }
public static boolean isRecordWithRootEnabled() {
return def().getBoolean(getString(R.string.key_record_with_root), false);
}
} }

View File

@@ -1,28 +1,229 @@
package com.stardust.scriptdroid.autojs.record; 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.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. * 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 Recorder mRecorder;
private CopyOnWriteArrayList<Recorder.OnStateChangedListener> mOnStateChangedListeners = new CopyOnWriteArrayList<>();
private TouchRecorder mTouchRecorder;
private Context mContext;
private boolean mDiscard = false;
private long mLastVolumeDownEventTime;
private ShellKeyObserver mShellKeyObserver;
@Override public static GlobalRecorder getSingleton(Context context) {
protected void startImpl() { 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() { 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);
} }
} }

View File

@@ -24,6 +24,7 @@ import com.stardust.scriptdroid.Pref;
import com.stardust.scriptdroid.R; import com.stardust.scriptdroid.R;
import com.stardust.scriptdroid.accessibility.AccessibilityEventHelper; import com.stardust.scriptdroid.accessibility.AccessibilityEventHelper;
import com.stardust.scriptdroid.autojs.AutoJs; 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.external.floatingwindow.menu.HoverMenuService;
import com.stardust.scriptdroid.ui.common.ScriptOperations; import com.stardust.scriptdroid.ui.common.ScriptOperations;
import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder; import com.stardust.theme.dialog.ThemeColorMaterialDialogBuilder;
@@ -31,6 +32,8 @@ import com.stardust.util.ClipboardUtil;
import com.stardust.util.MessageEvent; import com.stardust.util.MessageEvent;
import com.stardust.view.accessibility.AccessibilityService; import com.stardust.view.accessibility.AccessibilityService;
import com.stardust.view.accessibility.OnKeyListener; import com.stardust.view.accessibility.OnKeyListener;
import com.stardust.widget.PrefSwitch;
import com.stardust.widget.ViewSwitcher;
import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.Subscribe;
@@ -45,61 +48,36 @@ import io.mattcarroll.hover.NavigatorContent;
* Created by Stardust on 2017/3/12. * 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; private View mView;
@BindView(R.id.sw_recorded_by_root) @BindView(R.id.sw_recorded_by_root)
SwitchCompat mRecordedByRootSwitch; PrefSwitch mRecordedByRootSwitch;
@BindView(R.id.sw_record_toast) @BindView(R.id.sw_record_toast)
SwitchCompat mRecordToastSwitch; SwitchCompat mRecordToastSwitch;
@BindView(R.id.img_start_or_pause) @BindView(R.id.img_pause_or_resume)
ImageView mStartOrPauseRecordIcon; ImageView mPauseOrResumeImage;
@BindView(R.id.text_start_or_pause) @BindView(R.id.text_pause_or_resume)
TextView mStartOrPauseRecordText; TextView mPauseOrResumeText;
@BindView(R.id.stop_record) @BindView(R.id.view_switcher)
View mStopRecord; ViewSwitcher mViewSwitcher;
@BindView(R.id.discard_record) private GlobalRecorder mRecorder;
View mDiscardRecord;
private Recorder mRecorder;
private TouchRecorder mTouchRecorder;
private Context mContext; 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) { public RecordNavigatorContent(Context context) {
mContext = new ContextThemeWrapper(context, R.style.AppTheme); mContext = new ContextThemeWrapper(context, R.style.AppTheme);
mView = View.inflate(context, R.layout.floating_window_record, null); mView = View.inflate(context, R.layout.floating_window_record, null);
ButterKnife.bind(this, mView); ButterKnife.bind(this, mView);
HoverMenuService.getEventBus().register(this); HoverMenuService.getEventBus().register(this);
AccessibilityService.getStickOnKeyObserver().addListener(mVolumeKeyListener); mRecorder = GlobalRecorder.getSingleton(context);
mTouchRecorder = TouchRecorder.getGlobal(context); mRecorder.addOnStateChangedListener(this);
if (Pref.hasRecordTrigger()) { setState(mRecorder.getState());
mKeyObserver = new ShellKeyObserver();
mInputEventObserver.addListener(mKeyObserver);
mKeyObserver.setKeyListener(this);
}
} }
@NonNull @NonNull
@@ -128,57 +106,38 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
mRecordToastSwitch.toggle(); mRecordToastSwitch.toggle();
} }
@OnClick(R.id.start_or_pause) @OnClick(R.id.start_record)
void startOrPauseRecord() { void startRecord() {
if (mRecorder == null) { mRecorder.start();
startRecord(); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU));
} else if (mRecorder.getState() == Recorder.STATE_PAUSED) {
resumeRecord();
} else {
pauseRecord();
}
} }
@OnClick(R.id.discard_record) @OnClick(R.id.discard_record)
void discardRecord() { void discardRecord() {
mDiscard = true; mRecorder.discard();
stopRecord();
} }
private void resumeRecord() {
mRecorder.resume();
setState(Recorder.STATE_RECORDING);
HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU));
}
private void pauseRecord() { @OnClick(R.id.pause_or_resume_record)
mRecorder.pause(); void pauseOrResumeRecord() {
setState(Recorder.STATE_PAUSED); if (mRecorder.getState() == Recorder.STATE_PAUSED) {
} mRecorder.resume();
private void startRecord() {
mDiscard = false;
if (mRecordedByRootSwitch.isChecked()) {
mTouchRecorder.reset();
mRecorder = mTouchRecorder;
} else { } else {
mRecorder = AutoJs.getInstance().getAccessibilityActionRecorder(); mRecorder.pause();
} }
mRecorder.setOnStateChangedListener(this);
mRecorder.start();
setState(Recorder.STATE_RECORDING);
HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU)); HoverMenuService.postIntent(new Intent(HoverMenuService.ACTION_COLLAPSE_MENU));
} }
private void setState(int state) { private void setState(int state) {
mStopRecord.setVisibility(state == Recorder.STATE_STOPPED ? View.GONE : View.VISIBLE); if (state == Recorder.STATE_NOT_START || state == Recorder.STATE_STOPPED) {
mDiscardRecord.setVisibility(state == Recorder.STATE_STOPPED ? View.GONE : View.VISIBLE); mViewSwitcher.showFirst();
mStartOrPauseRecordIcon.setImageResource(state == Recorder.STATE_RECORDING ? R.drawable.ic_pause_white_24dp : R.drawable.ic_play_arrow_white_48dp); } else {
mStartOrPauseRecordText.setText( mViewSwitcher.showSecond();
state == Recorder.STATE_RECORDING ? R.string.text_pause_record : }
state == Recorder.STATE_PAUSED ? R.string.text_resume_record : mPauseOrResumeImage.setImageResource(state == Recorder.STATE_RECORDING ? R.drawable.ic_pause_white_24dp :
R.string.text_start_record); 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) @OnClick(R.id.stop_record)
@@ -192,8 +151,8 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
@Subscribe @Subscribe
public void onMessageEvent(MessageEvent event) { public void onMessageEvent(MessageEvent event) {
if (event.message.equals(HoverMenuService.ACTION_MENU_EXPANDING)) { if (event.message.equals(HoverMenuService.ACTION_MENU_EXPANDING)) {
if (mRecorder != null && mRecorder.getState() == Recorder.STATE_RECORDING) if (mRecorder.getState() == Recorder.STATE_RECORDING)
pauseRecord(); mRecorder.pause();
} else if (event.message.equals(HoverMenuService.ACTION_MENU_EXIT)) { } else if (event.message.equals(HoverMenuService.ACTION_MENU_EXIT)) {
onMenuExit(); onMenuExit();
} }
@@ -201,9 +160,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
public void onMenuExit() { public void onMenuExit() {
HoverMenuService.getEventBus().unregister(this); HoverMenuService.getEventBus().unregister(this);
AccessibilityService.getStickOnKeyObserver().addListener(mVolumeKeyListener); mRecorder.removeOnStateChangedListener(this);
mInputEventObserver.recycle();
mInputEventObserver.removeListener(mKeyObserver);
} }
@Subscribe @Subscribe
@@ -215,80 +172,21 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
@Override @Override
public void onStart() { public void onStart() {
App.getApp().getUiHandler().toast(R.string.text_start_record); setState(Recorder.STATE_RECORDING);
} }
@Override @Override
public void onStop() { public void onStop() {
if (!mDiscard) { setState(Recorder.STATE_STOPPED);
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);
} }
@Override @Override
public void onPause() { public void onPause() {
setState(Recorder.STATE_PAUSED);
} }
@Override @Override
public void onResume() { 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) {
}
}

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size
android:width="100dp"
android:height="0.77dp"/>
<solid android:color="#efefef"/>
</shape>

View File

@@ -11,7 +11,9 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:divider="@drawable/floating_menu_divider"
android:orientation="vertical"
android:showDividers="middle">
<TextView <TextView
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -21,10 +23,6 @@
android:textColor="@android:color/primary_text_dark" android:textColor="@android:color/primary_text_dark"
android:textSize="20sp"/> android:textSize="20sp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#c4c4c4"/>
<RelativeLayout <RelativeLayout
android:id="@+id/sw_root_container" android:id="@+id/sw_root_container"
@@ -49,17 +47,13 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_toLeftOf="@id/sw_recorded_by_root" android:layout_toLeftOf="@id/sw_recorded_by_root"
android:gravity="center_vertical"
android:text="@string/text_record_with_root" android:text="@string/text_record_with_root"
android:textColor="@android:color/primary_text_dark" android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"/> android:textSize="16sp"/>
</RelativeLayout> </RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#c4c4c4"/>
<RelativeLayout <RelativeLayout
android:id="@+id/sw_record_toast_container" android:id="@+id/sw_record_toast_container"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -87,104 +81,18 @@
</RelativeLayout> </RelativeLayout>
<View <com.stardust.widget.ViewSwitcher
android:layout_width="match_parent" android:id="@+id/view_switcher"
android:layout_height="1dp"
android:background="#c4c4c4"/>
<LinearLayout
android:id="@+id/start_or_pause"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground" android:measureAllChildren="false">
android:gravity="center_vertical"
android:paddingBottom="16dp"
android:paddingLeft="8dp"
android:paddingTop="16dp">
<ImageView <include layout="@layout/floating_window_record_start"/>
android:id="@+id/img_start_or_pause"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="8dp"
android:src="@drawable/ic_play_arrow_white_48dp"/>
<TextView <include layout="@layout/floating_window_record_pause"/>
android:id="@+id/text_start_or_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_start_record"
android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"/>
</LinearLayout> </com.stardust.widget.ViewSwitcher>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#c4c4c4"/>
<LinearLayout
android:id="@+id/stop_record"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:paddingBottom="16dp"
android:paddingLeft="8dp"
android:paddingTop="16dp"
android:visibility="gone">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="8dp"
android:src="@drawable/ic_stop_white_36pt"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_stop_record"
android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#c4c4c4"/>
<LinearLayout
android:id="@+id/discard_record"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:paddingBottom="16dp"
android:paddingLeft="8dp"
android:paddingTop="16dp"
android:visibility="gone">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="8dp"
android:src="@drawable/ic_close_white_24dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_discard_record"
android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"/>
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#c4c4c4"/>
</LinearLayout> </LinearLayout>

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="@drawable/floating_menu_divider"
android:orientation="vertical"
android:showDividers="middle|end">
<LinearLayout
android:id="@+id/pause_or_resume_record"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:paddingBottom="16dp"
android:paddingLeft="8dp"
android:paddingTop="16dp">
<ImageView
android:id="@+id/img_pause_or_resume"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="8dp"
android:src="@drawable/ic_pause_white_24dp"/>
<TextView
android:id="@+id/text_pause_or_resume"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_pause_record"
android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/stop_record"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:paddingBottom="16dp"
android:paddingLeft="8dp"
android:paddingTop="16dp">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="8dp"
android:src="@drawable/ic_stop_white_36pt"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_stop_record"
android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"/>
</LinearLayout>
<LinearLayout
android:id="@+id/discard_record"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:paddingBottom="16dp"
android:paddingLeft="8dp"
android:paddingTop="16dp">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="8dp"
android:src="@drawable/ic_close_white_24dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_discard_record"
android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:divider="@drawable/floating_menu_divider"
android:orientation="vertical"
android:showDividers="middle|end">
<LinearLayout
android:id="@+id/start_record"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:paddingBottom="16dp"
android:paddingLeft="8dp"
android:paddingTop="16dp">
<ImageView
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="8dp"
android:src="@drawable/ic_play_arrow_white_48dp"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_start_record"
android:textColor="@android:color/primary_text_dark"
android:textSize="16sp"/>
</LinearLayout>
</LinearLayout>

View File

@@ -4,6 +4,7 @@ import android.support.annotation.NonNull;
import static com.stardust.autojs.core.record.inputevent.InputEventRecorder.parseDeviceNumber; import static com.stardust.autojs.core.record.inputevent.InputEventRecorder.parseDeviceNumber;
/** /**
* Created by Stardust on 2017/7/20. * Created by Stardust on 2017/7/20.
*/ */

View File

@@ -38,7 +38,7 @@ public abstract class InputEventRecorder extends Recorder.AbstractRecorder imple
public abstract String getCode(); public abstract String getCode();
protected static int parseDeviceNumber(String device) { public static int parseDeviceNumber(String device) {
Matcher matcher = LAST_INT_PATTERN.matcher(device); Matcher matcher = LAST_INT_PATTERN.matcher(device);
if (matcher.find()) { if (matcher.find()) {
String someNumberStr = matcher.group(1); String someNumberStr = matcher.group(1);

View File

@@ -0,0 +1,32 @@
package com.stardust.event;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Created by Stardust on 2017/8/6.
*/
public class EventDispatcher<Listener> {
public interface Event<Listener> {
void notify(Listener l);
}
private CopyOnWriteArrayList<Listener> mListeners = new CopyOnWriteArrayList<>();
public void addListener(Listener l) {
mListeners.add(l);
}
public boolean removeListener(Listener l) {
return mListeners.remove(l);
}
public void dispatchEvent(Event<Listener> event) {
for (Listener listener : mListeners) {
event.notify(listener);
}
}
}