add: root record generates js file

fix: delay of tap, swipe by adding RootAutomator
This commit is contained in:
hyb1996
2017-08-13 11:12:14 +08:00
parent e96b6082bb
commit 9c5f876a8f
14 changed files with 267 additions and 123 deletions

View File

@@ -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"/>

View File

@@ -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");
}
} }

View File

@@ -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)

View File

@@ -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();
}
}
} }

View File

@@ -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 {

View File

@@ -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);

View File

@@ -82,7 +82,7 @@
<string name="text_join_qq_group">加入QQ互赞&amp;交流群</string> <string name="text_join_qq_group">加入QQ互赞&amp;交流群</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>

View File

@@ -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">

View File

@@ -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;
}
} }
} }

View File

@@ -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();
} }

View File

@@ -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();");
} }

View File

@@ -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);
} }

View File

@@ -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();

View File

@@ -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();
}
} }