add: root record generates js file
fix: delay of tap, swipe by adding RootAutomator
This commit is contained in:
@@ -186,7 +186,6 @@
|
|||||||
<data android:mimeType="application/plain"/>
|
<data android:mimeType="application/plain"/>
|
||||||
<data android:mimeType="application/x-javascript"/>
|
<data android:mimeType="application/x-javascript"/>
|
||||||
<data android:mimeType="text/*"/>
|
<data android:mimeType="text/*"/>
|
||||||
<data android:mimeType="*/*" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND"/>
|
<action android:name="android.intent.action.SEND"/>
|
||||||
@@ -210,9 +209,9 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".external.open.ImportIntentActivity"
|
android:name=".external.open.ImportIntentActivity"
|
||||||
android:theme="@style/AppTheme.Transparent"
|
|
||||||
android:icon="@drawable/ic_import"
|
android:icon="@drawable/ic_import"
|
||||||
android:label="@string/text_import_script">
|
android:label="@string/text_import_script"
|
||||||
|
android:theme="@style/AppTheme.Transparent">
|
||||||
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.VIEW"/>
|
<action android:name="android.intent.action.VIEW"/>
|
||||||
@@ -228,8 +227,6 @@
|
|||||||
<data android:mimeType="application/plain"/>
|
<data android:mimeType="application/plain"/>
|
||||||
<data android:mimeType="application/x-javascript"/>
|
<data android:mimeType="application/x-javascript"/>
|
||||||
<data android:mimeType="text/*"/>
|
<data android:mimeType="text/*"/>
|
||||||
<data android:mimeType="*/*" />
|
|
||||||
<data android:mimeType="application/*" android:host="*" android:pathPattern=".*.auto" android:scheme="content" />
|
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.SEND"/>
|
<action android:name="android.intent.action.SEND"/>
|
||||||
|
|||||||
@@ -128,4 +128,9 @@ public class Pref {
|
|||||||
public static boolean isRecordToastEnabled() {
|
public static boolean isRecordToastEnabled() {
|
||||||
return def().getBoolean(getString(R.string.key_record_toast), true);
|
return def().getBoolean(getString(R.string.key_record_toast), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean rootRecordGeneratesBinary() {
|
||||||
|
return def().getString(getString(R.string.key_root_record_out_file_type), "binary")
|
||||||
|
.equals("binary");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ import com.stardust.autojs.core.inputevent.InputEventCodes;
|
|||||||
import com.stardust.autojs.core.inputevent.ShellKeyObserver;
|
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.accessibility.AccessibilityActionRecorder;
|
import com.stardust.autojs.core.record.accessibility.AccessibilityActionRecorder;
|
||||||
|
import com.stardust.autojs.core.record.inputevent.InputEventRecorder;
|
||||||
|
import com.stardust.autojs.core.record.inputevent.InputEventToAutoFileRecorder;
|
||||||
|
import com.stardust.autojs.core.record.inputevent.InputEventToRootAutomatorRecorder;
|
||||||
import com.stardust.autojs.core.record.inputevent.TouchRecorder;
|
import com.stardust.autojs.core.record.inputevent.TouchRecorder;
|
||||||
import com.stardust.autojs.runtime.api.Shell;
|
import com.stardust.autojs.runtime.api.Shell;
|
||||||
import com.stardust.scriptdroid.App;
|
import com.stardust.scriptdroid.App;
|
||||||
@@ -45,8 +48,6 @@ public class GlobalRecorder implements Recorder.OnStateChangedListener {
|
|||||||
private TouchRecorder mTouchRecorder;
|
private TouchRecorder mTouchRecorder;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private boolean mDiscard = false;
|
private boolean mDiscard = false;
|
||||||
private long mLastVolumeDownEventTime;
|
|
||||||
private ShellKeyObserver mShellKeyObserver;
|
|
||||||
|
|
||||||
public static GlobalRecorder getSingleton(Context context) {
|
public static GlobalRecorder getSingleton(Context context) {
|
||||||
if (sSingleton == null) {
|
if (sSingleton == null) {
|
||||||
@@ -55,61 +56,25 @@ public class GlobalRecorder implements Recorder.OnStateChangedListener {
|
|||||||
return sSingleton;
|
return sSingleton;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void initSingleton(Context context) {
|
public static void initSingleton(Context context) {
|
||||||
getSingleton(context);
|
getSingleton(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GlobalRecorder(Context context) {
|
public GlobalRecorder(Context context) {
|
||||||
mContext = new ContextThemeWrapper(context.getApplicationContext(), R.style.AppTheme);
|
mContext = new ContextThemeWrapper(context.getApplicationContext(), R.style.AppTheme);
|
||||||
mTouchRecorder = new TouchRecorder(context);
|
mTouchRecorder = new TouchRecorder(context) {
|
||||||
addKeyListeners();
|
@Override
|
||||||
|
protected InputEventRecorder createInputEventRecorder() {
|
||||||
|
if (Pref.rootRecordGeneratesBinary())
|
||||||
|
return new InputEventToAutoFileRecorder(mContext);
|
||||||
|
else
|
||||||
|
return new InputEventToRootAutomatorRecorder();
|
||||||
|
}
|
||||||
|
};
|
||||||
EventBus.getDefault().register(this);
|
EventBus.getDefault().register(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (!Pref.isRecordVolumeControlEnable()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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() {
|
public void start() {
|
||||||
if (Pref.isRecordWithRootEnabled()) {
|
if (Pref.isRecordWithRootEnabled()) {
|
||||||
@@ -139,6 +104,10 @@ public class GlobalRecorder implements Recorder.OnStateChangedListener {
|
|||||||
return mRecorder.getCode();
|
return mRecorder.getCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getPath() {
|
||||||
|
return mRecorder.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
public int getState() {
|
public int getState() {
|
||||||
if (mRecorder == null)
|
if (mRecorder == null)
|
||||||
return Recorder.STATE_NOT_START;
|
return Recorder.STATE_NOT_START;
|
||||||
@@ -166,7 +135,11 @@ public class GlobalRecorder implements Recorder.OnStateChangedListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
if (!mDiscard) {
|
if (!mDiscard) {
|
||||||
handleRecordedScript(getCode());
|
String code = getCode();
|
||||||
|
if (code != null)
|
||||||
|
handleRecordedScript(code);
|
||||||
|
else
|
||||||
|
handleRecordedFile(getPath());
|
||||||
}
|
}
|
||||||
for (Recorder.OnStateChangedListener listener : mOnStateChangedListeners) {
|
for (Recorder.OnStateChangedListener listener : mOnStateChangedListeners) {
|
||||||
listener.onStop();
|
listener.onStop();
|
||||||
@@ -212,6 +185,23 @@ public class GlobalRecorder implements Recorder.OnStateChangedListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleRecordedFile(final String path) {
|
||||||
|
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||||
|
App.getApp().getUiHandler().post(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
handleRecordedFile(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
new ScriptOperations(mContext, null)
|
||||||
|
.importFile(path)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void showRecordHandleDialog(final String script) {
|
private void showRecordHandleDialog(final String script) {
|
||||||
DialogUtils.showDialog(new ThemeColorMaterialDialogBuilder(mContext)
|
DialogUtils.showDialog(new ThemeColorMaterialDialogBuilder(mContext)
|
||||||
.title(R.string.text_recorded)
|
.title(R.string.text_recorded)
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ 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 {
|
public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStateChangedListener, OnKeyListener {
|
||||||
|
|
||||||
private View mView;
|
private View mView;
|
||||||
@BindView(R.id.sw_recorded_by_root)
|
@BindView(R.id.sw_recorded_by_root)
|
||||||
@@ -68,18 +68,36 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
|
|||||||
|
|
||||||
private GlobalRecorder mRecorder;
|
private GlobalRecorder mRecorder;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
|
private long mLastVolumeDownEventTime;
|
||||||
|
|
||||||
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(mContext, R.layout.floating_window_record, null);
|
||||||
ButterKnife.bind(this, mView);
|
ButterKnife.bind(this, mView);
|
||||||
HoverMenuService.getEventBus().register(this);
|
HoverMenuService.getEventBus().register(this);
|
||||||
mRecorder = GlobalRecorder.getSingleton(context);
|
mRecorder = GlobalRecorder.getSingleton(context);
|
||||||
mRecorder.addOnStateChangedListener(this);
|
mRecorder.addOnStateChangedListener(this);
|
||||||
setState(mRecorder.getState());
|
setState(mRecorder.getState());
|
||||||
|
AccessibilityService.getStickOnKeyObserver().addListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onVolumeDown() {
|
||||||
|
if (!Pref.isRecordVolumeControlEnable()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (System.currentTimeMillis() - mLastVolumeDownEventTime < 300) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mLastVolumeDownEventTime = System.currentTimeMillis();
|
||||||
|
int state = mRecorder.getState();
|
||||||
|
if (state == Recorder.STATE_RECORDING || state == Recorder.STATE_PAUSED) {
|
||||||
|
mRecorder.stop();
|
||||||
|
} else {
|
||||||
|
mRecorder.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public View getView() {
|
public View getView() {
|
||||||
@@ -161,6 +179,7 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
|
|||||||
public void onMenuExit() {
|
public void onMenuExit() {
|
||||||
HoverMenuService.getEventBus().unregister(this);
|
HoverMenuService.getEventBus().unregister(this);
|
||||||
mRecorder.removeOnStateChangedListener(this);
|
mRecorder.removeOnStateChangedListener(this);
|
||||||
|
AccessibilityService.getStickOnKeyObserver().removeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -182,4 +201,13 @@ public class RecordNavigatorContent implements NavigatorContent, Recorder.OnStat
|
|||||||
public void onResume() {
|
public void onResume() {
|
||||||
setState(Recorder.STATE_RECORDING);
|
setState(Recorder.STATE_RECORDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onKeyEvent(int keyCode, KeyEvent event) {
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_DOWN &&
|
||||||
|
(keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
|
||||||
|
onVolumeDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@ import android.text.TextUtils;
|
|||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.stardust.pio.PFile;
|
||||||
import com.stardust.scriptdroid.ui.BaseActivity;
|
import com.stardust.scriptdroid.ui.BaseActivity;
|
||||||
import com.stardust.scriptdroid.ui.common.ScriptOperations;
|
import com.stardust.scriptdroid.ui.common.ScriptOperations;
|
||||||
import com.stardust.scriptdroid.ui.main.MainActivity;
|
import com.stardust.scriptdroid.ui.main.MainActivity;
|
||||||
@@ -45,9 +46,13 @@ public class ImportIntentActivity extends BaseActivity {
|
|||||||
private void handleIntent(Intent intent) throws FileNotFoundException {
|
private void handleIntent(Intent intent) throws FileNotFoundException {
|
||||||
Uri uri = intent.getData();
|
Uri uri = intent.getData();
|
||||||
if (uri != null && "content".equals(uri.getScheme())) {
|
if (uri != null && "content".equals(uri.getScheme())) {
|
||||||
|
String ext = PFile.getExtension(uri.getScheme());
|
||||||
|
if(TextUtils.isEmpty(ext)){
|
||||||
|
ext = "js";
|
||||||
|
}
|
||||||
InputStream stream = getContentResolver().openInputStream(uri);
|
InputStream stream = getContentResolver().openInputStream(uri);
|
||||||
new ScriptOperations(this, null)
|
new ScriptOperations(this, null)
|
||||||
.importFile("", stream)
|
.importFile("", stream, ext)
|
||||||
.subscribe(new Consumer<String>() {
|
.subscribe(new Consumer<String>() {
|
||||||
@Override
|
@Override
|
||||||
public void accept(@NonNull String s) throws Exception {
|
public void accept(@NonNull String s) throws Exception {
|
||||||
|
|||||||
@@ -96,7 +96,13 @@ public class ScriptOperations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void newScriptFile() {
|
public void newScriptFile() {
|
||||||
newScriptFileForScript(null);
|
showFileNameInputDialog("", "js")
|
||||||
|
.subscribe(new Consumer<String>() {
|
||||||
|
@Override
|
||||||
|
public void accept(@io.reactivex.annotations.NonNull String input) throws Exception {
|
||||||
|
createScriptFile(getCurrentDirectoryPath() + input + ".js", null, true);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Observable<String> importFile(final String pathFrom) {
|
public Observable<String> importFile(final String pathFrom) {
|
||||||
@@ -117,13 +123,13 @@ public class ScriptOperations {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Observable<String> importFile(String prefix, final InputStream inputStream) {
|
public Observable<String> importFile(String prefix, final InputStream inputStream, final String ext) {
|
||||||
return showFileNameInputDialog(PFile.getNameWithoutExtension(prefix), "js")
|
return showFileNameInputDialog(PFile.getNameWithoutExtension(prefix), ext)
|
||||||
.observeOn(Schedulers.io())
|
.observeOn(Schedulers.io())
|
||||||
.map(new Function<String, String>() {
|
.map(new Function<String, String>() {
|
||||||
@Override
|
@Override
|
||||||
public String apply(@io.reactivex.annotations.NonNull String s) throws Exception {
|
public String apply(@io.reactivex.annotations.NonNull String s) throws Exception {
|
||||||
final String pathTo = getCurrentDirectoryPath() + s + ".js";
|
final String pathTo = getCurrentDirectoryPath() + s + "." + ext;
|
||||||
if (PFile.copyStream(inputStream, pathTo)) {
|
if (PFile.copyStream(inputStream, pathTo)) {
|
||||||
showMessage(R.string.text_import_succeed);
|
showMessage(R.string.text_import_succeed);
|
||||||
} else {
|
} else {
|
||||||
@@ -198,14 +204,13 @@ public class ScriptOperations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private CharSequence getString(int resId) {
|
private CharSequence getString(int resId) {
|
||||||
return mContext.getString(resId);
|
return mContext.getString(resId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Observable<String> importSample(Sample sample) {
|
public Observable<String> importSample(Sample sample) {
|
||||||
try {
|
try {
|
||||||
return importFile(sample.name, mContext.getAssets().open(sample.path));
|
return importFile(sample.name, mContext.getAssets().open(sample.path), PFile.getExtension(sample.path));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
showMessage(R.string.text_import_fail);
|
showMessage(R.string.text_import_fail);
|
||||||
|
|||||||
@@ -82,7 +82,7 @@
|
|||||||
<string name="text_join_qq_group">加入QQ互赞&交流群</string>
|
<string name="text_join_qq_group">加入QQ互赞&交流群</string>
|
||||||
<string name="text_copied">已复制到剪贴板</string>
|
<string name="text_copied">已复制到剪贴板</string>
|
||||||
<string name="text_use_volume_control_record">使用音量下键控制</string>
|
<string name="text_use_volume_control_record">使用音量下键控制</string>
|
||||||
<string name="summary_use_volume_control_record">开启后每次音量下键会开始或停止脚本录制</string>
|
<string name="summary_use_volume_control_record">开启悬浮窗后每次音量下键会开始或停止脚本录制</string>
|
||||||
<string name="key_use_volume_control_record">key_use_volume_control_record</string>
|
<string name="key_use_volume_control_record">key_use_volume_control_record</string>
|
||||||
<string name="text_mobile_qq_not_installed">未安装手机QQ</string>
|
<string name="text_mobile_qq_not_installed">未安装手机QQ</string>
|
||||||
<string name="text_edit">编辑</string>
|
<string name="text_edit">编辑</string>
|
||||||
@@ -218,6 +218,9 @@
|
|||||||
<string name="text_import_script">导入脚本文件</string>
|
<string name="text_import_script">导入脚本文件</string>
|
||||||
<string name="key_record_with_root">key_record_with_root</string>
|
<string name="key_record_with_root">key_record_with_root</string>
|
||||||
<string name="key_record_toast">key_record_toast</string>
|
<string name="key_record_toast">key_record_toast</string>
|
||||||
|
<string name="key_root_record_out_file_type">key_root_record_out_file_type</string>
|
||||||
|
<string name="text_root_record_out_file_type">Root录制生成文件类型</string>
|
||||||
|
<string name="text_binary">binary</string>
|
||||||
|
|
||||||
|
|
||||||
<string-array name="record_control_keys">
|
<string-array name="record_control_keys">
|
||||||
@@ -246,4 +249,14 @@
|
|||||||
<item>Off</item>
|
<item>Off</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
|
|
||||||
|
|
||||||
|
<string-array name="root_record_out_file_type_keys">
|
||||||
|
<item>可编辑的js文件</item>
|
||||||
|
<item>不可编辑的二进制文件</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<string-array name="root_record_out_file_type_values">
|
||||||
|
<item>js</item>
|
||||||
|
<item>binary</item>
|
||||||
|
</string-array>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -18,6 +18,14 @@
|
|||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:key="@string/key_record_toast"
|
android:key="@string/key_record_toast"
|
||||||
android:title="@string/text_record_msg"/>
|
android:title="@string/text_record_msg"/>
|
||||||
|
|
||||||
|
<com.afollestad.materialdialogs.prefs.MaterialListPreference
|
||||||
|
android:defaultValue="@string/text_binary"
|
||||||
|
android:entries="@array/root_record_out_file_type_keys"
|
||||||
|
android:entryValues="@array/root_record_out_file_type_values"
|
||||||
|
android:key="@string/key_root_record_out_file_type"
|
||||||
|
android:title="@string/text_root_record_out_file_type"
|
||||||
|
/>
|
||||||
</com.stardust.theme.preference.ThemeColorPreferenceCategory>
|
</com.stardust.theme.preference.ThemeColorPreferenceCategory>
|
||||||
|
|
||||||
<com.stardust.theme.preference.ThemeColorPreferenceCategory android:title="@string/text_script_running">
|
<com.stardust.theme.preference.ThemeColorPreferenceCategory android:title="@string/text_script_running">
|
||||||
|
|||||||
@@ -55,6 +55,8 @@ public interface Recorder {
|
|||||||
|
|
||||||
String getCode();
|
String getCode();
|
||||||
|
|
||||||
|
String getPath();
|
||||||
|
|
||||||
int getState();
|
int getState();
|
||||||
|
|
||||||
void setOnStateChangedListener(OnStateChangedListener onStateChangedListener);
|
void setOnStateChangedListener(OnStateChangedListener onStateChangedListener);
|
||||||
@@ -171,5 +173,9 @@ public interface Recorder {
|
|||||||
mOnStateChangedListener = onStateChangedListener == null ? NO_OPERATION_LISTENER : onStateChangedListener;
|
mOnStateChangedListener = onStateChangedListener == null ? NO_OPERATION_LISTENER : onStateChangedListener;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,11 @@ public class InputEventToAutoFileRecorder extends InputEventRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getCode() {
|
public String getCode() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
return mTmpFile.getAbsolutePath();
|
return mTmpFile.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class InputEventToRootAutomatorRecorder extends InputEventRecorder {
|
|||||||
private int mLastTouchY = -1;
|
private int mLastTouchY = -1;
|
||||||
|
|
||||||
public InputEventToRootAutomatorRecorder() {
|
public InputEventToRootAutomatorRecorder() {
|
||||||
mCode.append("var ra = new RootAutomator();\n")
|
mCode.append("var ra = new RootAutomator(context);\n")
|
||||||
.append("ra.setScreenMetrics(").append(getDeviceScreenWidth()).append(", ")
|
.append("ra.setScreenMetrics(").append(getDeviceScreenWidth()).append(", ")
|
||||||
.append(getDeviceScreenHeight()).append(");\n");
|
.append(getDeviceScreenHeight()).append(");\n");
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ public class InputEventToRootAutomatorRecorder extends InputEventRecorder {
|
|||||||
if (mLastEventTime == 0) {
|
if (mLastEventTime == 0) {
|
||||||
mLastEventTime = event.time;
|
mLastEventTime = event.time;
|
||||||
} else if (event.time - mLastEventTime > 0.001) {
|
} else if (event.time - mLastEventTime > 0.001) {
|
||||||
mCode.append("ra.sleep(").append((long) (1000L * (event.time - mLastEventTime))).append(");\n");
|
mCode.append("sleep(").append((long) (1000L * (event.time - mLastEventTime))).append(");\n");
|
||||||
mLastEventTime = event.time;
|
mLastEventTime = event.time;
|
||||||
}
|
}
|
||||||
int device = parseDeviceNumber(event.device);
|
int device = parseDeviceNumber(event.device);
|
||||||
@@ -96,7 +96,7 @@ public class InputEventToRootAutomatorRecorder extends InputEventRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setTouchDevice(int i) {
|
private void setTouchDevice(int i) {
|
||||||
mCode.append("ra.setInputDevice(").append(i).append(");\n");
|
//mCode.append("ra.setInputDevice(").append(i).append(");\n");
|
||||||
mTouchDevice = i;
|
mTouchDevice = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ public class InputEventToRootAutomatorRecorder extends InputEventRecorder {
|
|||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
super.stop();
|
super.stop();
|
||||||
mCode.append("log(ra.writeToDevice(context));");
|
mCode.append("ra.exit();");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import com.stardust.autojs.core.record.Recorder;
|
|||||||
|
|
||||||
public class TouchRecorder extends Recorder.AbstractRecorder {
|
public class TouchRecorder extends Recorder.AbstractRecorder {
|
||||||
|
|
||||||
private static TouchRecorder sInstance;
|
|
||||||
private InputEventRecorder mInputEventRecorder;
|
private InputEventRecorder mInputEventRecorder;
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private InputEventObserver mInputEventObserver;
|
private InputEventObserver mInputEventObserver;
|
||||||
@@ -27,19 +26,17 @@ public class TouchRecorder extends Recorder.AbstractRecorder {
|
|||||||
mInputEventObserver.observe();
|
mInputEventObserver.observe();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TouchRecorder getGlobal(Context context) {
|
|
||||||
if (sInstance == null)
|
|
||||||
sInstance = new TouchRecorder(context);
|
|
||||||
return sInstance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void startImpl() {
|
protected void startImpl() {
|
||||||
mInputEventRecorder = new InputEventToAutoFileRecorder(mContext);
|
mInputEventRecorder = createInputEventRecorder();
|
||||||
mInputEventObserver.addListener(mInputEventRecorder);
|
mInputEventObserver.addListener(mInputEventRecorder);
|
||||||
mInputEventRecorder.start();
|
mInputEventRecorder.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected InputEventRecorder createInputEventRecorder() {
|
||||||
|
return new InputEventToAutoFileRecorder(mContext);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void pauseImpl() {
|
protected void pauseImpl() {
|
||||||
super.pauseImpl();
|
super.pauseImpl();
|
||||||
@@ -64,6 +61,11 @@ public class TouchRecorder extends Recorder.AbstractRecorder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPath() {
|
||||||
|
return mInputEventRecorder.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
public void reset() {
|
public void reset() {
|
||||||
setState(STATE_NOT_START);
|
setState(STATE_NOT_START);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,18 +33,8 @@ public class RootAutomatorEngine extends ScriptEngine.AbstractScriptEngine<AutoF
|
|||||||
|
|
||||||
public RootAutomatorEngine(Context context, String deviceNameOrPath) {
|
public RootAutomatorEngine(Context context, String deviceNameOrPath) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
if (sTouchDevice < 0) {
|
mDeviceNameOrPath = getDeviceNameOrPath(context, deviceNameOrPath);
|
||||||
sTouchDevice = PreferenceManager.getDefaultSharedPreferences(context).getInt(KEY_TOUCH_DEVICE, -1);
|
|
||||||
}
|
|
||||||
if (sTouchDevice >= 0) {
|
|
||||||
mDeviceNameOrPath = "/dev/input/event" + sTouchDevice;
|
|
||||||
PreferenceManager.getDefaultSharedPreferences(context)
|
|
||||||
.edit()
|
|
||||||
.putInt(KEY_TOUCH_DEVICE, sTouchDevice)
|
|
||||||
.apply();
|
|
||||||
} else {
|
|
||||||
mDeviceNameOrPath = deviceNameOrPath;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -62,7 +52,22 @@ public class RootAutomatorEngine extends ScriptEngine.AbstractScriptEngine<AutoF
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String getExecutablePath(Context context) {
|
|
||||||
|
public static String getDeviceNameOrPath(Context context, String deviceNameOrPath) {
|
||||||
|
if (sTouchDevice < 0) {
|
||||||
|
sTouchDevice = PreferenceManager.getDefaultSharedPreferences(context).getInt(KEY_TOUCH_DEVICE, -1);
|
||||||
|
}
|
||||||
|
if (sTouchDevice >= 0) {
|
||||||
|
deviceNameOrPath = "/dev/input/event" + sTouchDevice;
|
||||||
|
PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.edit()
|
||||||
|
.putInt(KEY_TOUCH_DEVICE, sTouchDevice)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
return deviceNameOrPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getExecutablePath(Context context) {
|
||||||
File tmp = new File(context.getCacheDir(), "root_automator");
|
File tmp = new File(context.getCacheDir(), "root_automator");
|
||||||
PFile.copyAsset(context, ROOT_AUTOMATOR_EXECUTABLE_ASSET, tmp.getAbsolutePath());
|
PFile.copyAsset(context, ROOT_AUTOMATOR_EXECUTABLE_ASSET, tmp.getAbsolutePath());
|
||||||
return tmp.getAbsolutePath();
|
return tmp.getAbsolutePath();
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
package com.stardust.autojs.runtime.api;
|
package com.stardust.autojs.runtime.api;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.SystemClock;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.InputDevice;
|
||||||
|
|
||||||
|
import com.stardust.autojs.core.inputevent.InputDevices;
|
||||||
import com.stardust.autojs.engine.RootAutomatorEngine;
|
import com.stardust.autojs.engine.RootAutomatorEngine;
|
||||||
|
import com.stardust.autojs.runtime.ScriptRuntime;
|
||||||
|
import com.stardust.autojs.runtime.exception.ScriptInterruptedException;
|
||||||
import com.stardust.pio.UncheckedIOException;
|
import com.stardust.pio.UncheckedIOException;
|
||||||
import com.stardust.util.ScreenMetrics;
|
import com.stardust.util.ScreenMetrics;
|
||||||
|
|
||||||
@@ -19,6 +26,7 @@ import static com.stardust.autojs.core.inputevent.InputEventCodes.*;
|
|||||||
|
|
||||||
public class RootAutomator {
|
public class RootAutomator {
|
||||||
|
|
||||||
|
private static final String LOG_TAG = "RootAutomator";
|
||||||
|
|
||||||
public static final byte DATA_TYPE_SLEEP = 0;
|
public static final byte DATA_TYPE_SLEEP = 0;
|
||||||
public static final byte DATA_TYPE_EVENT = 1;
|
public static final byte DATA_TYPE_EVENT = 1;
|
||||||
@@ -26,31 +34,22 @@ public class RootAutomator {
|
|||||||
public static final byte DATA_TYPE_EVENT_TOUCH_X = 3;
|
public static final byte DATA_TYPE_EVENT_TOUCH_X = 3;
|
||||||
public static final byte DATA_TYPE_EVENT_TOUCH_Y = 4;
|
public static final byte DATA_TYPE_EVENT_TOUCH_Y = 4;
|
||||||
|
|
||||||
private DataOutputStream mTmpFileOutputStream;
|
@Nullable
|
||||||
private File mEventTmpFile;
|
|
||||||
private ScreenMetrics mScreenMetrics;
|
private ScreenMetrics mScreenMetrics;
|
||||||
private String mDevicePath;
|
private AbstractShell mShell;
|
||||||
|
private int mDefaultId = -1;
|
||||||
|
|
||||||
public RootAutomator() {
|
public RootAutomator(Context context) {
|
||||||
try {
|
mShell = new ProcessShell(true);
|
||||||
mEventTmpFile = File.createTempFile(String.valueOf(System.currentTimeMillis()), ".auto");
|
String path = RootAutomatorEngine.getExecutablePath(context);
|
||||||
mEventTmpFile.deleteOnExit();
|
String deviceNameOrPath = RootAutomatorEngine.getDeviceNameOrPath(context, InputDevices.getTouchDeviceName());
|
||||||
mTmpFileOutputStream = new DataOutputStream(new FileOutputStream(mEventTmpFile));
|
mShell.exec("chmod 777 " + path);
|
||||||
} catch (IOException e) {
|
mShell.exec(path + " -d " + deviceNameOrPath);
|
||||||
throw new UncheckedIOException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void sendEvent(int type, int code, int value) throws IOException {
|
public void sendEvent(int type, int code, int value) throws IOException {
|
||||||
mTmpFileOutputStream.writeByte(DATA_TYPE_EVENT);
|
mShell.exec(type + " " + code + " " + value);
|
||||||
mTmpFileOutputStream.writeShort(type);
|
|
||||||
mTmpFileOutputStream.writeShort(code);
|
|
||||||
mTmpFileOutputStream.writeInt(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setInputDevice(int i) throws IOException {
|
|
||||||
mDevicePath = "/dev/input/event" + i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void touch(int x, int y) throws IOException {
|
public void touch(int x, int y) throws IOException {
|
||||||
@@ -70,6 +69,8 @@ public class RootAutomator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int scaleX(int x) {
|
private int scaleX(int x) {
|
||||||
|
if (mScreenMetrics == null)
|
||||||
|
return x;
|
||||||
return mScreenMetrics.scaleX(x);
|
return mScreenMetrics.scaleX(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,43 +79,117 @@ public class RootAutomator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void sendSync() throws IOException {
|
public void sendSync() throws IOException {
|
||||||
sendEvent(0, 0, 0);
|
sendEvent(EV_SYN, SYN_REPORT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMtSync() throws IOException {
|
||||||
|
sendEvent(EV_SYN, SYN_MT_REPORT, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int scaleY(int y) {
|
private int scaleY(int y) {
|
||||||
|
if (mScreenMetrics == null)
|
||||||
|
return y;
|
||||||
return mScreenMetrics.scaleY(y);
|
return mScreenMetrics.scaleY(y);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void sleep(int n) throws IOException {
|
public void tap(int x, int y, int id) throws IOException {
|
||||||
mTmpFileOutputStream.writeByte(DATA_TYPE_SLEEP);
|
touchDown(x, y, id);
|
||||||
mTmpFileOutputStream.writeInt(n);
|
touchUp(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void tap(int x, int y) throws IOException {
|
public void tap(int x, int y) throws IOException {
|
||||||
//sendEvent(EV_ABS, ABS_MT_TRACKING_ID, 0x0000398c);
|
sendEvent(x, y, mDefaultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swipe(int x1, int y1, int x2, int y2, int duration, int id) throws IOException {
|
||||||
|
long now = SystemClock.uptimeMillis();
|
||||||
|
touchDown(x1, y1, id);
|
||||||
|
long startTime = now;
|
||||||
|
long endTime = startTime + duration;
|
||||||
|
while (now < endTime) {
|
||||||
|
long elapsedTime = now - startTime;
|
||||||
|
float alpha = (float) elapsedTime / duration;
|
||||||
|
touchMove((int) lerp(x1, x2, alpha), (int) lerp(y1, y2, alpha), id);
|
||||||
|
now = SystemClock.uptimeMillis();
|
||||||
|
}
|
||||||
|
touchUp(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swipe(int x1, int y1, int x2, int y2, int duration) throws IOException {
|
||||||
|
swipe(x1, y1, x2, y2, duration, mDefaultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swipe(int x1, int y1, int x2, int y2) throws IOException {
|
||||||
|
swipe(x1, y1, x2, y2, 300, mDefaultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void touchDown(int x, int y, int id) throws IOException {
|
||||||
|
sendEvent(EV_ABS, ABS_MT_TRACKING_ID, id);
|
||||||
sendEvent(EV_KEY, BTN_TOUCH, 0x00000001);
|
sendEvent(EV_KEY, BTN_TOUCH, 0x00000001);
|
||||||
sendEvent(EV_KEY, BTN_TOOL_FINGER, 0x00000001);
|
sendEvent(EV_KEY, BTN_TOOL_FINGER, 0x00000001);
|
||||||
sendEvent(EV_ABS, ABS_MT_POSITION_X, x);
|
sendEvent(EV_ABS, ABS_MT_POSITION_X, scaleX(x));
|
||||||
sendEvent(EV_ABS, ABS_MT_POSITION_Y, y);
|
sendEvent(EV_ABS, ABS_MT_POSITION_Y, scaleY(y));
|
||||||
|
sendEvent(EV_ABS, ABS_MT_TOUCH_MAJOR, 5);
|
||||||
sendEvent(EV_SYN, SYN_REPORT, 0x00000000);
|
sendEvent(EV_SYN, SYN_REPORT, 0x00000000);
|
||||||
//sendEvent(EV_ABS, ABS_MT_TRACKING_ID, 0xffffffff);
|
}
|
||||||
|
|
||||||
|
public void touchDown(int x, int y) throws IOException {
|
||||||
|
touchDown(x, y, mDefaultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void touchUp(int id) throws IOException {
|
||||||
|
sendEvent(EV_ABS, ABS_MT_TRACKING_ID, id);
|
||||||
sendEvent(EV_KEY, BTN_TOUCH, 0x00000000);
|
sendEvent(EV_KEY, BTN_TOUCH, 0x00000000);
|
||||||
sendEvent(EV_KEY, BTN_TOOL_FINGER, 0x00000000);
|
sendEvent(EV_KEY, BTN_TOOL_FINGER, 0x00000000);
|
||||||
sendEvent(EV_SYN, SYN_REPORT, 0x00000000);
|
sendEvent(EV_SYN, SYN_REPORT, 0x00000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void swipe(int x, int y, int duration){
|
public void touchUp() throws IOException {
|
||||||
|
touchUp(mDefaultId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AbstractShell.Result writeToDevice(Context context) {
|
public void touchMove(int x, int y, int id) throws IOException {
|
||||||
if (mDevicePath == null) {
|
sendEvent(EV_ABS, ABS_MT_TRACKING_ID, id);
|
||||||
return new RootAutomatorEngine(context).execute(mEventTmpFile.getAbsolutePath());
|
sendEvent(EV_ABS, ABS_MT_POSITION_X, scaleX(x));
|
||||||
} else {
|
sendEvent(EV_ABS, ABS_MT_POSITION_Y, scaleY(y));
|
||||||
return new RootAutomatorEngine(context, mDevicePath).execute(mEventTmpFile.getAbsolutePath());
|
sendEvent(EV_SYN, SYN_REPORT, 0x00000000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void touchMove(int x, int y) throws IOException {
|
||||||
|
touchMove(x, y, mDefaultId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDefaultId() {
|
||||||
|
return mDefaultId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultId(int defaultId) {
|
||||||
|
mDefaultId = defaultId;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sleep(long duration) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(duration);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new ScriptInterruptedException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static float lerp(float a, float b, float alpha) {
|
||||||
|
return (b - a) * alpha + a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void exit() throws IOException {
|
||||||
|
sendEvent(0xffff, 0xffff, 0xefefefef);
|
||||||
|
mShell.exec("exit");
|
||||||
|
mShell.exec("exit");
|
||||||
|
mShell.exec("exit");
|
||||||
|
mShell.exec("exit");
|
||||||
|
mShell.exec("exit");
|
||||||
|
mShell.exec("exit");
|
||||||
|
mShell.exit();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user