random click and gesture for android 7.0+!!!

This commit is contained in:
hyb1996 2017-05-16 21:40:23 +08:00
parent ec98d75d3b
commit c4ec951a10
42 changed files with 757 additions and 564 deletions

View File

@ -89,7 +89,7 @@
android:name=".ui.error.IssueReporterActivity"
android:theme="@style/IssueReporterTheme"/>
<service android:name="com.stardust.view.Floaty$FloatHeadService"/>
<service android:name="com.stardust.scriptdroid.external.ScriptExecutionIntentService"/>
<service android:name="com.stardust.scriptdroid.external.floating_window.menu.HoverMenuService"/>
<activity android:name=".external.tasker.TaskPrefEditActivity"/>

View File

@ -39,6 +39,11 @@
"type": "markdown",
"path":"documentation"
},
{
"title": "Android7.0以上点按与手势模拟",
"type": "markdown",
"path": "documentation"
},
{
"title": "模块与第三方jar",
"type": "markdown",

View File

@ -0,0 +1,79 @@
在Android7.0及以上可以不需要root权限做到任意坐标的点击滑动和手势模拟并且比使用root权限的延迟更短。
**注意以下命令只有Android7.0及以上才有效**
### click(x, y)
* x \<Number\> 要点击的坐标的x值
* y \<Number\> 要点击的坐标的y值
模拟点击坐标(x, y),并返回是否点击成功。只有在点击执行完成后脚本才继续执行。
一般而言,只有点击过程(大约150毫秒)中被其他事件中断(例如用户自行点击)才会点击失败。
> 可以在开发者选项中启用指针位置来查看坐标
### longClick(x, y)
* x \<Number\> 要长按的坐标的x值
* y \<Number\> 要长按的坐标的y值
模拟长按坐标(x, y), 并 返回是否成功。只有在长按执行完成大约600毫秒时脚本才会继续执行。
一般而言,只有长按过程中被其他事件中断(例如用户自行点击)才会长按失败。
### press(x, y, duration)
* x \<Number\> 要按住的坐标的x值
* y \<Number\> 要按住的坐标的y值
* duration \<Number\> 按住时长,单位毫秒
模拟按住坐标(x, y), 并返回是否成功。只有按住操作执行完成时脚本才会继续执行。
如果按住时间过短那么会被系统认为是点击如果时长超过500毫秒则认为是长按。
一般而言,只有按住过程中被其他事件中断才会操作失败。
### swipe(x1, y1, x2, y2, duration)
* x1 \<Number\> 滑动的起始坐标的x值
* y1 \<Number\> 滑动的起始坐标的y值
* x2 \<Number\> 滑动的结束坐标的x值
* y2 \<Number\> 滑动的结束坐标的y值
* duration \<Number\> 滑动时长,单位毫秒
模拟从坐标(x1, y1)滑动到坐标(x2, y2),并返回是否成功。只有滑动操作执行完成时脚本才会继续执行。
一般而言,只有滑动过程中被其他事件中断才会滑动失败。
### gesture(duration, \[x1, y1\], \[x2, y2\], ...)
* duration \<Number\> 手势的时长
* \[x, y\] \<Array\> 手势滑动路径的一系列坐标
模拟手势操作。例如`gesture(1000, [0, 0], [500, 500], [500, 1000])`为模拟一个从(0, 0)到(500, 500)到(500, 100)的手势操作时长为2秒。
### gestures(\[delay, duration, \[x1, y1\], \[x2, y2\], ...], \[delay, duration, \[x3, y3\], \[x4, y4\], ...\], ...)
同时模拟多个手势。每个手势的参数为\[delay, duration, 坐标\], delay为延迟多久(毫秒)才执行该手势duration为手势执行时长坐标为手势经过的点的坐标。其中delay参数可以省略默认为0。
参见示例。
### setScreenMetrics(width, height)
* width \<Number\> 屏幕宽度,单位像素
* height <\Number\> 屏幕高度单位像素
设置脚本坐标点击所适合的屏幕宽高。如果脚本运行时,屏幕宽度不一致会自动放缩坐标。
例如在1920*1080的设备中某个操作的代码为
```
setScreenMetrics(1080, 1920);
click(800, 200);
longClick(300, 500);
```
那么在其他设备上AutoJs会自动放缩坐标以便脚本仍然有效。

View File

@ -0,0 +1,11 @@
"auto";
launchApp("QQ");
sleep(2000);
while(true){
if(currentPackage() == 'com.tencent.mobileqq'){
swipe(600, 700, 600, 1400, 400);
}
sleep(300);
}

View File

@ -0,0 +1,14 @@
"auto";
/**
* 同时模拟三个手势
* (300, 400)(300, 1400)
* (600, 400)(600, 1400)
* (900, 400)(900, 1400)
* 每一个的时长都为350毫秒
*/
gestures([350, [300, 400], [300, 1400]],
[350, [600, 400], [600, 1400]],
[350, [900, 400], [900, 1400]]
);

View File

@ -0,0 +1,10 @@
"auto";
setScreenMetrics(1080, 1920);
//如果你使用的是MIUI此脚本运行后会出现桌面多屏幕编辑
home();
sleep(1500);
gestures([350, [800, 300], [500, 1000]],
[350, [300, 1500], [500, 1000]],
[350, [300, 300], [500, 1000]]);

View File

@ -0,0 +1,9 @@
"auto";
setScreenMetrics(1080, 1920);
//如果你使用的是MIUI此脚本运行后会出现桌面编辑
home();
sleep(1500);
gestures([500, [800, 300], [500, 1000]],
[500, [300, 1500], [500, 1000]]);

View File

@ -0,0 +1,19 @@
"auto";
toast("开启开发者选项-指针位置或者在画画软件才能查看效果");
setScreenMetrics(1080, 1920);
var points = [10000];
var interval = 0.1;
var x0 = 600;
var y0 = 1000;
var a = 120;
for(var t = 0; t < 2 * Math.PI; t += interval){
var x = x0 + a * (2 * Math.cos(t) - Math.cos(2 * t));
var y = y0 + a * (2 * Math.sin(t) - Math.sin(2 * t));
points.push([parseInt(x), parseInt(y)]);
}
gesture.apply(null, points);

View File

@ -0,0 +1,4 @@
"auto";
//表示从位置(500, 10)滑动到位置(500, 1000), 持续两秒
swipe(500, 10, 500, 1000, 2000);

View File

@ -0,0 +1,5 @@
"auto";
setScreenMetrics(1080, 1920);
click(100, 150);

View File

@ -1,24 +1,21 @@
"ui";
importClass(android.widget.EditText);
importClass(android.widget.Button);
importClass(android.widget.LinearLayout);
importClass(android.content.Intent);
importClass(android.net.Uri);
ui.statusBarColor("#03a9f4");
var qq = new EditText(activity);
qq.setHint("请输入QQ号");
var btnOk = new Button(activity);
btnOk.setText("确定");
var container = new LinearLayout(activity);
container.addView(qq);
container.addView(btnOk);
activity.setContentView(container);
ui.layout(
<vertical>
<text w="*" h="56" bg="#03a9f4" paddingLeft="16" gravity="center_vertical" color="#fff" size="18sp">QQ打开聊天窗口</text>
<vertical h="*" w="*" margin="16 50">
<input id="qq" h="50" hint="请输入QQ号"/>
<button id="ok" text="确定" />
</vertical>
</vertical>
)
btnOk.setOnClickListener(function(view){
ui.ok.click(function(){
app.startActivity({
action: "android.intent.action.VIEW",
data: "mqqwpa://im/chat?chat_type=wpa&uin=" + qq.getText()
data: "mqqwpa://im/chat?chat_type=wpa&uin=" + ui.qq.text()
});
activity.finish();
ui.finish();
});

View File

@ -0,0 +1,67 @@
"ui";
/**
* By Da Zhang
* 本脚本仅为娱乐没有任何破坏性质
*/
ui.statusBarColor("#AA0000");
var Quin = 32552732;
ui.layout(
<frame background="#AA0000">
<vertical align="top" paddingTop="5" margin="10">
<text id="oops" color="#FFFFFF" gravity="center" size="20">Oops, your files have been encrypted!</text>
<text id="text" bg="#FFFFFF" gravity="left" color="#000" size="15" marginTop="15" h="425"></text>
<button id="payment" text="Payment" margin="20 0 0 0"/>
<button id="decrypt" text="Decrypt"/>
</vertical>
</frame>
);
ui.text.text("我的手机出了什么问题?\n您的一些重要文件被我加密保存了。\n" +
"照片、图片、文档、压缩包、音频、视频文件、apk文件等几乎所有类型的文件都被加密了因此不能正常打开。\n" +
"这和一般文件损坏有本质上的区别。您大可在网上找找恢复文件的方法,我敢保证,没有我们的解密服务,就算老天爷来了也不能恢复这些文档。\n\n" +
"有没有恢复这些文档的方法?\n当然有可恢复的方法。只能通过我们的解密服务才能恢复。我以人格担保能够提供安全有效的恢复服务。\n" +
"但这是收费的,也不能无限期的推迟。\n请点击 <Decrypt> 按钮,就可以免费恢复一些文档。请您放心,我是绝不会骗你的。\n" +
"但想要恢复全部文档,需要付款点费用。\n是否随时都可以固定金额付款就会恢复的吗当然不是推迟付款时间越长对你不利。\n" +
"最好3天之内付款费用过了三天费用就会翻倍。\n还有一个礼拜之内未付款将会永远恢复不了。\n" +
"对了,忘了告诉你,对半年以上没钱付款的穷人,会有活动免费恢复,能否轮到你,就要看您的运气怎么样了。");
ui.oops.click(() => toast("Fuck you!"));
ui.oops.longClick(() => {
var thisjoke="This is a joke : )";
if(ui.oops.text() != thisjoke){
ui.oops.text(thisjoke);
}else{
ui.oops.text("Oops, your files have been encrypted!");
}
return true;
});
ui.text.click(() => ui.text.append("。"));
ui.text.longClick(() => {
ui.text.setText("\n"+ui.text.getText())
return true;
});
ui.payment.click(() => {
try{
app.startActivity({
action:"android.intent.action.VIEW",
data:"mqqapi://card/show_pslcard?&uin=" + Quin
});
toast("Please payment by QQ");
}catch(e){
toast("Payment Error");
}
});
ui.payment.longClick(() => {
toast("You are silly b!");
return true;
});
ui.decrypt.click(() => {
toast("Decrypt Error");
activity.finish();
});
ui.decrypt.longClick(() => {
toast("You can't decrypt!");
return true;
});

View File

@ -11,7 +11,7 @@ import android.view.WindowManager;
import com.afollestad.materialdialogs.DialogAction;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.autojs.runtime.api.internal.VolatileBox;
import com.stardust.concurrent.VolatileBox;
import com.stardust.util.UiHandler;
/**

View File

@ -8,7 +8,7 @@ import android.view.ContextThemeWrapper;
import com.afollestad.materialdialogs.MaterialDialog;
import com.stardust.autojs.runtime.ScriptInterface;
import com.stardust.autojs.runtime.api.AppUtils;
import com.stardust.autojs.runtime.api.internal.VolatileBox;
import com.stardust.concurrent.VolatileBox;
import com.stardust.scriptdroid.R;
import com.stardust.util.ArrayUtils;
import com.stardust.util.UiHandler;

View File

@ -3,6 +3,7 @@ package com.stardust.scriptdroid.external;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import com.stardust.autojs.script.FileScriptSource;
import com.stardust.autojs.script.ScriptSource;
@ -32,6 +33,7 @@ public class CommonUtils {
String directoryPath = null;
String script = intent.getStringExtra(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT);
ScriptSource source = null;
Toast.makeText(context, path, Toast.LENGTH_SHORT).show();
if (path == null && script != null) {
source = new StringScriptSource(script);
} else if (path != null && new PathChecker(context).checkAndToastError(path)) {

View File

@ -0,0 +1,23 @@
package com.stardust.scriptdroid.external;
import android.app.IntentService;
import android.content.Intent;
import android.support.annotation.Nullable;
/**
* Created by Stardust on 2017/5/15.
*/
public class ScriptExecutionIntentService extends IntentService {
public ScriptExecutionIntentService() {
super("ScriptExecutionIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent == null)
return;
CommonUtils.handleIntent(this, intent);
}
}

View File

@ -5,8 +5,8 @@ import android.support.annotation.NonNull;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static com.stardust.util.ScreenMetrics.getScreenHeight;
import static com.stardust.util.ScreenMetrics.getScreenWidth;
import static com.stardust.util.ScreenMetrics.getDeviceScreenHeight;
import static com.stardust.util.ScreenMetrics.getDeviceScreenWidth;
/**
* Created by Stardust on 2017/5/3.
@ -23,8 +23,8 @@ public class InputEventToSendEventJsConverter extends InputEventConverter {
public InputEventToSendEventJsConverter() {
mCode.append("var sh = new Shell(true);\n")
.append("sh.SetScreenMetrics(").append(getScreenWidth()).append(", ")
.append(getScreenHeight()).append(");\n");
.append("sh.SetScreenMetrics(").append(getDeviceScreenWidth()).append(", ")
.append(getDeviceScreenHeight()).append(");\n");
}

View File

@ -7,6 +7,7 @@ import android.support.annotation.NonNull;
import com.stardust.scriptdroid.App;
import com.stardust.scriptdroid.external.CommonUtils;
import com.stardust.scriptdroid.external.ScriptExecutionIntentService;
import com.stardust.scriptdroid.external.open.RunIntentActivity;
import com.twofortyfouram.locale.sdk.client.receiver.AbstractPluginSettingReceiver;
@ -30,9 +31,8 @@ public class FireSettingReceiver extends AbstractPluginSettingReceiver {
@Override
protected void firePluginSetting(@NonNull Context context, @NonNull Bundle bundle) {
CommonUtils.handleIntent(context, new Intent(App.getApp(), RunIntentActivity.class)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.putExtra(CommonUtils.EXTRA_KEY_PATH, bundle.getString(CommonUtils.EXTRA_KEY_PATH))
.putExtra(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT, bundle.getString(CommonUtils.EXTRA_KEY_PRE_EXECUTE_SCRIPT)));
context.startService(new Intent(context, ScriptExecutionIntentService.class)
.putExtras(bundle));
}
}

View File

@ -1,10 +1,14 @@
package com.stardust.scriptdroid.service;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import android.view.KeyEvent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import com.nickandjerry.dynamiclayoutinflator.lib.DynamicLayoutInflator;
import com.stardust.scriptdroid.tool.AccessibilityServiceTool;
import com.stardust.view.accessibility.AccessibilityDelegate;
import com.stardust.view.accessibility.AccessibilityService;
@ -49,6 +53,9 @@ public class AccessibilityWatchDogService extends AccessibilityService {
@Override
public void onAccessibilityEvent(final AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED) {
}
Log.v(TAG, "onAccessibilityEvent: " + event);
if (!containsAllEventTypes && !eventTypes.contains(event.getEventType()))
return;
@ -64,6 +71,18 @@ public class AccessibilityWatchDogService extends AccessibilityService {
}
}
@Override
protected boolean onKeyEvent(KeyEvent event) {
Log.v(TAG, "onKeyEvent: " + event);
return super.onKeyEvent(event);
}
@Override
protected boolean onGesture(int gestureId) {
Log.v(TAG, "onGesture: " + gestureId);
return super.onGesture(gestureId);
}
@Override
public void onInterrupt() {
}
@ -76,7 +95,7 @@ public class AccessibilityWatchDogService extends AccessibilityService {
@Override
protected void onServiceConnected() {
Log.v(TAG, "onServiceConnected");
Log.v(TAG, "onServiceConnected: " + getServiceInfo().toString());
instance = this;
super.onServiceConnected();
synchronized (LOCK) {

View File

@ -7,6 +7,7 @@ import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;

View File

@ -1,442 +0,0 @@
package com.stardust.view;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.support.v4.view.GestureDetectorCompat;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.GestureDetector;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.LinearLayout;
/**
* Created by ericbhatti on 11/24/15.
* <p>
* Modified by Stardust on 2017/3/10
*
* @author Eric Bhatti
* @since 24 November, 2015
*/
public class Floaty {
public interface FloatyOrientationListener {
/**
* This method is called before the orientation change happens, you can use this to save the data of your views so you can later populate the data back in {@link #afterOrientationChange}
*
* @param floaty The floating window
*/
public void beforeOrientationChange(Floaty floaty);
/**
* This method is called after the orientation change happens, you can use this to restore the data of your views that you saved in {@link #beforeOrientationChange}
*
* @param floaty The floating window
*/
public void afterOrientationChange(Floaty floaty);
}
public interface OnBackPressedListener {
boolean onBackPressed();
}
private View.OnClickListener mOnHeadClickListener;
private OnBackPressedListener mOnBackPressedListener;
private final View head;
private final View body;
private final Context context;
private final Notification notification;
private final int notificationId;
private static Floaty floaty;
private final FloatyOrientationListener floatyOrientationListener;
private float ratioY = 0;
private float oldWidth = 0;
private float oldX = 0;
private boolean confChange = false;
private static final String LOG_TAG = "Floaty";
/**
* @return The body of the floaty which is assigned through the {@link #createInstance} method.
*/
public View getBody() {
return floaty.body;
}
/**
* @return The head of the floaty which is assigned through the {@link #createInstance} method.
*/
public View getHead() {
return floaty.head;
}
/**
* Creates a Singleton of the Floating Window
*
* @param context The application context
* @param head The head View, upon clicking it the body is to be opened
* @param body The body View
* @param notificationId The notificationId for your notification
* @param notification The notification which is displayed for the foreground service
* @param floatyOrientationListener The {@link FloatyOrientationListener} interface with callbacks which are called when orientation changes.
* @return A Floating Window
*/
public static synchronized Floaty createInstance(Context context, View head, View body, int notificationId, Notification notification, FloatyOrientationListener
floatyOrientationListener) {
if (floaty == null) {
floaty = new Floaty(context, head, body, notificationId, notification, floatyOrientationListener);
}
return floaty;
}
/**
* Creates a Singleton of the Floating Window
*
* @param context The application context
* @param head The head View, upon clicking it the body is to be opened
* @param body The body View
* @param notificationId The notificationId for your notification
* @param notification The notification which is displayed for the foreground service
* @return A Floating Window
*/
public static synchronized Floaty createInstance(Context context, View head, View body, int notificationId, Notification notification) {
if (floaty == null) {
floaty = new Floaty(context, head, body, notificationId, notification, new FloatyOrientationListener() {
@Override
public void beforeOrientationChange(Floaty floaty) {
Log.d(LOG_TAG, "beforeOrientationChange");
}
@Override
public void afterOrientationChange(Floaty floaty) {
Log.d(LOG_TAG, "afterOrientationChange");
}
});
}
return floaty;
}
public static synchronized Floaty createInstance(Context context, View head, View body) {
return createInstance(context, head, body, -1, null);
}
/**
* @return The same instance of Floating Window, which has been created through {@link #createInstance}. Don't call this method before createInstance
*/
public static synchronized Floaty getInstance() {
if (floaty == null) {
throw new NullPointerException("Floaty not initialized! First call createInstance method, then to access Floaty in any other class call getDefault()");
}
return floaty;
}
private Floaty(Context context, View head, View body, int notificationId, Notification notification, FloatyOrientationListener floatyOrientationListener) {
this.head = head;
this.body = body;
this.context = context;
this.notification = notification;
this.notificationId = notificationId;
this.floatyOrientationListener = floatyOrientationListener;
}
/**
* Starts the service and adds it to the screen
*/
public void startService() {
Intent intent = new Intent(context, Floaty.FloatHeadService.class);
context.startService(intent);
}
/**
* Stops the service and removes it from the screen
*/
public void stopService() {
Intent intent = new Intent(context, Floaty.FloatHeadService.class);
context.stopService(intent);
}
public void setOnHeadClickListener(View.OnClickListener onHeadClickListener) {
mOnHeadClickListener = onHeadClickListener;
}
public void setOnBackPressedListener(OnBackPressedListener onBackPressedListener) {
mOnBackPressedListener = onBackPressedListener;
}
/**
* Helper method for notification creation.
*
* @param context
* @param contentTitle
* @param contentText
* @param notificationIcon
* @param contentIntent
* @return Notification for the Service
*/
public static Notification createNotification(Context context, String contentTitle, String contentText, int notificationIcon, PendingIntent contentIntent) {
return new NotificationCompat.Builder(context)
.setContentTitle(contentTitle)
.setContentText(contentText)
.setSmallIcon(notificationIcon)
.setContentIntent(contentIntent).build();
}
public static class FloatHeadService extends Service {
private WindowManager windowManager;
private WindowManager.LayoutParams params;
private LinearLayout mLinearLayout;
GestureDetectorCompat gestureDetectorCompat;
DisplayMetrics metrics;
private boolean didFling;
private int[] clickLocation = new int[2];
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE || newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
int[] location = new int[2];
mLinearLayout.getLocationOnScreen(location);
floaty.oldWidth = metrics.widthPixels;
floaty.confChange = true;
if (floaty.getBody().getVisibility() == View.VISIBLE) {
floaty.oldX = clickLocation[0];
floaty.ratioY = (float) (clickLocation[1]) / (float) metrics.heightPixels;
} else {
floaty.oldX = location[0];
floaty.ratioY = (float) (location[1]) / (float) metrics.heightPixels;
}
floaty.floatyOrientationListener.beforeOrientationChange(floaty);
floaty.stopService();
floaty.startService();
floaty.floatyOrientationListener.afterOrientationChange(floaty);
}
}
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(LOG_TAG, "onStartCommand");
metrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(metrics);
if (floaty.notification != null)
startForeground(floaty.notificationId, floaty.notification);
return START_STICKY;
}
private void showHead() {
floaty.head.setVisibility(View.VISIBLE);
floaty.body.setVisibility(View.GONE);
params.x = clickLocation[0];
params.y = clickLocation[1] - 36;
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
mLinearLayout.setBackgroundColor(Color.argb(0, 0, 0, 0));
windowManager.updateViewLayout(mLinearLayout, params);
}
@Override
public void onCreate() {
super.onCreate();
Log.d(LOG_TAG, "onCreate");
mLinearLayout = new LinearLayout(getApplicationContext()) {
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
if (floaty.mOnBackPressedListener != null && !floaty.mOnBackPressedListener.onBackPressed()) {
showHead();
return true;
}
}
if (event.getKeyCode() == KeyEvent.KEYCODE_HOME) {
showHead();
return true;
}
return super.dispatchKeyEvent(event);
}
};
gestureDetectorCompat = new GestureDetectorCompat(floaty.context, new GestureDetector.SimpleOnGestureListener() {
private int initialX;
private int initialY;
private float initialTouchX;
private float initialTouchY;
@Override
public boolean onDown(MotionEvent event) {
Log.d(LOG_TAG, "onDown");
initialX = params.x;
initialY = params.y;
initialTouchX = event.getRawX();
initialTouchY = event.getRawY();
didFling = false;
return false;
}
@Override
public void onShowPress(MotionEvent e) {
Log.d(LOG_TAG, "onShowPress");
floaty.head.setAlpha(0.8f);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if (floaty.body.getVisibility() == View.VISIBLE) {
floaty.body.setVisibility(View.GONE);
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
mLinearLayout.setBackgroundColor(Color.argb(0, 0, 0, 0));
}
params.x = (initialX + (int) ((e2.getRawX() - initialTouchX)));
params.y = (initialY + (int) ((e2.getRawY() - initialTouchY)));
windowManager.updateViewLayout(mLinearLayout, params);
return false;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
Log.d(LOG_TAG, "onSingleTapConfirmed");
if (floaty.body.getVisibility() == View.GONE) {
params.x = metrics.widthPixels;
params.y = 0;
params.flags = params.flags & ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
//floaty.head.getLocationOnScreen(clickLocation);
floaty.head.setVisibility(View.GONE);
floaty.body.setVisibility(View.VISIBLE);
mLinearLayout.setBackgroundColor(Color.argb(200, 50, 50, 50));
} else {
floaty.body.setVisibility(View.GONE);
floaty.head.setVisibility(View.VISIBLE);
params.x = clickLocation[0];
params.y = clickLocation[1] - 36;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
mLinearLayout.setBackgroundColor(Color.argb(0, 0, 0, 0));
}
windowManager.updateViewLayout(mLinearLayout, params);
if (floaty.mOnHeadClickListener != null) {
floaty.mOnHeadClickListener.onClick(floaty.head);
}
return false;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.d(LOG_TAG, "onFling");
didFling = true;
int newX = params.x;
if (newX > (metrics.widthPixels / 2))
params.x = metrics.widthPixels;
else
params.x = 0;
windowManager.updateViewLayout(mLinearLayout, params);
return false;
}
});
mLinearLayout.setOrientation(LinearLayout.VERTICAL);
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
metrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(metrics);
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_PHONE,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP | Gravity.LEFT;
if (floaty.confChange) {
floaty.confChange = false;
if (floaty.oldX < (floaty.oldWidth / 2)) {
params.x = 0;
} else {
params.x = metrics.widthPixels;
}
params.y = (int) (metrics.heightPixels * floaty.ratioY);
} else {
params.x = metrics.widthPixels;
params.y = 0;
}
floaty.body.setVisibility(View.GONE);
floaty.head.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetectorCompat.onTouchEvent(event);
if (event.getAction() == MotionEvent.ACTION_UP) {
floaty.head.setAlpha(1.0f);
if (!didFling) {
Log.d(LOG_TAG, "ACTION_UP");
int newX = params.x;
if (newX > (metrics.widthPixels / 2))
params.x = metrics.widthPixels;
else
params.x = 0;
windowManager.updateViewLayout(mLinearLayout, params);
}
}
return true;
}
});
windowManager.addView(mLinearLayout, params);
if (floaty.body.getParent() != null) {
((ViewGroup) floaty.body.getParent()).removeView(floaty.body);
}
mLinearLayout.setFocusable(true);
LinearLayout.LayoutParams headParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
LinearLayout.LayoutParams bodyParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
headParams.gravity = Gravity.TOP | Gravity.RIGHT;
bodyParams.gravity = Gravity.TOP;
mLinearLayout.addView(floaty.head, headParams);
mLinearLayout.addView(floaty.body, bodyParams);
}
public void onDestroy() {
super.onDestroy();
if (mLinearLayout != null) {
mLinearLayout.removeAllViews();
windowManager.removeView(mLinearLayout);
}
stopForeground(true);
}
}
}

View File

@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityEventTypes="typeNotificationStateChanged|typeAnnouncement|typeAssistReadingContext|typeContextClicked|typeGestureDetectionEnd|typeGestureDetectionStart|typeTouchExplorationGestureEnd|typeTouchExplorationGestureStart|typeTouchInteractionEnd|typeTouchInteractionStart|typeViewAccessibilityFocusCleared|typeViewAccessibilityFocused|typeViewClicked|typeViewHoverEnter|typeViewHoverExit|typeViewFocused|typeViewLongClicked|typeViewScrolled|typeViewSelected|typeViewTextChanged|typeViewTextSelectionChanged|typeViewTextTraversedAtMovementGranularity|typeWindowContentChanged|typeWindowsChanged|typeWindowStateChanged"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagIncludeNotImportantViews|flagReportViewIds|flagRetrieveInteractiveWindows"
android:accessibilityFlags="flagIncludeNotImportantViews|flagReportViewIds|flagRetrieveInteractiveWindows|flagRequestEnhancedWebAccessibility|flagRequestFilterKeyEvents"
android:canPerformGestures="true"
android:canRequestEnhancedWebAccessibility="true"
android:canRequestTouchExplorationMode="true"
android:canRetrieveWindowContent="true"
android:description="@string/text_accessibility_service_description"
android:notificationTimeout="100"/>

View File

@ -32,18 +32,18 @@ import static org.junit.Assert.assertTrue;
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public class ExampleUnitTest {
// TODO: 2017/3/3 自定义函数
// TODO: 2017/3/3 自定义函数 ×
// TODO: 2017/3/19 exist函数
// TODO: 2017/3/23 tasker插件
// TODO: 2017/3/23 任务管理与控制台
// TODO: 2017/3/23 悬浮窗加入控制台
// TODO: 2017/3/24 文件读写api
// TODO: 2017/3/23 悬浮窗加入控制台
// TODO: 2017/3/24 文件读写api ---
// TODO: 2017/3/24 网络读写api
// TODO: 2017/3/24 常驻后台api
// TODO: 2017/3/24 uiE4x
// TODO: 2017/3/24 编辑界面文档和自动补全
// TODO: 2017/3/24 驻留模式
//// TODO: 2017/3/26 NODEJS
// TODO: 2017/3/24 常驻后台api ×
// TODO: 2017/3/24 uiE4x ---
// TODO: 2017/3/24 编辑界面文档和自动补全 ×
// TODO: 2017/3/24 驻留模式 ×
//// TODO: 2017/3/26 NODEJS ×
// TODO: 2017/3/31 自定义快捷方式图标

View File

@ -13,17 +13,60 @@ module.exports = function(__runtime__, scope){
}
automator.click = function(){
if(arguments.length >= 2 && typeof(arguments[0]) == 'number' && typeof(arguments[1]) == 'number'){
return __runtime__.automator.click(arguments[0], arguments[1]);
}
return performAction(function(target){
return __runtime__.automator.click(target);
}, arguments);
}
automator.longClick = function(a, b, c, d){
if(arguments.length == 2 && typeof(arguments[0]) == 'number' && typeof(arguments[1]) == 'number'){
return __runtime__.automator.longClick(arguments[0], arguments[1]);
}
return performAction(function(target){
return __runtime__.automator.longClick(target);
}, arguments);
}
automator.press = __runtime__.automator.press.bind(__runtime__.automator);
automator.gesture = __runtime__.automator.gesture.bind(__runtime__.automator, 0);
automator.gestureAsync = __runtime__.automator.gestureAsync.bind(__runtime__.automator, 0);
automator.swipe = __runtime__.automator.swipe.bind(__runtime__.automator);
automator.gestures = function(){
return __runtime__.automator.gestures(toStrokes(arguments));
}
automator.gesturesAsync = function(){
__runtime__.automator.gesturesAsync(toStrokes(arguments));
}
function toStrokes(args){
var screenMetrics = __runtime__.getScreenMetrics();
var len = args.length;
var strokes = [];
for(var i = 0; i < len; i++){
var gesture = args[i];
var pointsIndex = 1;
if(typeof(gesture[1]) == 'number'){
var start = gesture[0];
var delay = gesture[1];
pointsIndex = 2;
}else{
var start = 0;
var delay = gesture[0];
}
var gestureLen = gesture.length;
var path = new android.graphics.Path();
path.moveTo(screenMetrics.scaleX(gesture[pointsIndex][0]), screenMetrics.scaleY(gesture[pointsIndex][1]));
for(var j = pointsIndex + 1; j < gestureLen; j++){
path.lineTo(screenMetrics.scaleX(gesture[j][0]), screenMetrics.scaleY(gesture[j][1]));
}
strokes.push(new android.accessibilityservice.GestureDescription.StrokeDescription(path, start, delay));
}
return strokes;
}
automator.scrollDown = function(a, b, c, d){
if(arguments.length == 0)
@ -54,7 +97,7 @@ module.exports = function(__runtime__, scope){
}
scope.__asGlobal__(__runtime__.automator, ['back', 'home', 'powerDialog', 'notifications', 'quickSettings', 'recents', 'splitScreen']);
scope.__asGlobal__(automator, ['click', 'longClick', 'scrollDown', 'scrollUp', 'input']);
scope.__asGlobal__(automator, ['click', 'longClick', 'press', 'swipe', 'gesture', 'gestures', 'gestureAsync', 'gesturesAsync', 'scrollDown', 'scrollUp', 'input']);
return automator;
}

View File

@ -42,4 +42,6 @@ module.exports = function(__runtime__, scope){
sleep(delay);
}
}
scope.setScreenMetrics = __runtime__.setScreenMetrics.bind(__runtime__);
}

View File

@ -1,5 +1,5 @@
module.exports = function(__runtime__, scope){
var ui = Object(__runtime__.ui);
var ui = Object.create(__runtime__.ui);
ui.__id_cache__ = {};
ui.layout = function(xml){
@ -39,7 +39,13 @@ module.exports = function(__runtime__, scope){
if(typeof(color) == 'string'){
color = android.graphics.Color.parseColor(color);
}
activity.getWindow().setStatusBarColor(color);
if(Build.VERSION.SDK_INT >= 21){
activity.getWindow().setStatusBarColor(color);
}
}
ui.finish = function(){
activity.finish();
}
function decorate(view){

View File

@ -23,6 +23,11 @@ import com.stardust.util.UiHandler;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.PrintWriter;
import java.util.LinkedHashSet;
import java.util.Set;
@ -57,7 +62,7 @@ public class ScriptEngineService {
e.printStackTrace();
onFinish(execution);
if (!causedByInterrupted(e)) {
execution.getRuntime().console.error(e.getMessage());
execution.getRuntime().console.error(getScriptTrace(e));
EVENT_BUS.post(new ScriptExecutionEvent(ScriptExecutionEvent.ON_EXCEPTION, e.getMessage()));
}
}
@ -249,4 +254,25 @@ public class ScriptEngineService {
return mMessage;
}
}
private static String getScriptTrace(Exception e) {
try {
PipedReader reader = new PipedReader(8192);
PrintWriter writer = new PrintWriter(new PipedWriter(reader));
e.printStackTrace(writer);
writer.close();
BufferedReader bufferedReader = new BufferedReader(reader);
String line;
StringBuilder scriptTrace = new StringBuilder(e.getMessage());
while ((line = bufferedReader.readLine()) != null) {
if (line.trim().startsWith("at script"))
scriptTrace.append("\n").append(line);
}
return scriptTrace.toString();
} catch (IOException e1) {
e1.printStackTrace();
return e.getMessage();
}
}
}

View File

@ -12,6 +12,8 @@ import com.stardust.pio.UncheckedIOException;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.ErrorReporter;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.ImporterTopLevel;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
@ -61,7 +63,7 @@ public class RhinoJavaScriptEngine implements ScriptEngine {
Reader reader = source.getNonNullScriptReader();
try {
reader = preprocess(reader);
return mContext.evaluateReader(mScriptable, reader, "<script>", 1, null);
return mContext.evaluateReader(mScriptable, reader, "<" + source.getName() + ">", 1, null);
} catch (IOException e) {
throw new UncheckedIOException(e);
}

View File

@ -9,6 +9,7 @@ import com.stardust.autojs.runtime.api.Console;
import com.stardust.autojs.runtime.api.UiSelector;
import com.stardust.autojs.runtime.api.ui.UI;
import com.stardust.autojs.runtime.simple_action.SimpleActionAutomator;
import com.stardust.util.ScreenMetrics;
import com.stardust.view.accessibility.AccessibilityInfoProvider;
/**
@ -73,6 +74,12 @@ public abstract class AbstractScriptRuntime {
@ScriptInterface
public abstract void stop();
@ScriptInterface
public abstract void setScreenMetrics(int width, int height);
@ScriptInterface
public abstract ScreenMetrics getScreenMetrics();
public abstract void ensureAccessibilityServiceEnabled();
public abstract void onStop();

View File

@ -1,21 +1,22 @@
package com.stardust.autojs.runtime;
import android.content.Intent;
import android.os.Build;
import android.os.Looper;
import com.stardust.autojs.R;
import com.stardust.autojs.engine.ScriptEngine;
import com.stardust.autojs.engine.preprocess.Preprocessor;
import com.stardust.autojs.rhino_android.AndroidClassLoader;
import com.stardust.autojs.runtime.api.AbstractShell;
import com.stardust.autojs.runtime.api.AppUtils;
import com.stardust.autojs.runtime.api.Console;
import com.stardust.autojs.runtime.api.UiSelector;
import com.stardust.autojs.runtime.api.internal.VolatileBox;
import com.stardust.concurrent.VolatileBox;
import com.stardust.autojs.runtime.api.ui.UI;
import com.stardust.pio.UncheckedIOException;
import com.stardust.util.ClipboardUtil;
import com.stardust.autojs.runtime.api.ProcessShell;
import com.stardust.util.ScreenMetrics;
import com.stardust.util.SdkVersionUtil;
import com.stardust.util.Supplier;
import com.stardust.util.UiHandler;
@ -88,6 +89,7 @@ public class ScriptRuntime extends AbstractScriptRuntime {
private AbstractShell mRootShell;
private Supplier<AbstractShell> mShellSupplier;
private ScreenMetrics mScreenMetrics = new ScreenMetrics();
protected ScriptRuntime(Builder builder) {
@ -98,6 +100,7 @@ public class ScriptRuntime extends AbstractScriptRuntime {
if (ui == null) {
ui = new UI(mUiHandler.getContext());
}
automator.setScreenMetrics(mScreenMetrics);
}
public UiHandler getUiHandler() {
@ -155,6 +158,7 @@ public class ScriptRuntime extends AbstractScriptRuntime {
throw new ScriptInterruptedException();
}
mRootShell = mShellSupplier.get();
mRootShell.SetScreenMetrics(mScreenMetrics);
mShellSupplier = null;
}
}
@ -186,7 +190,19 @@ public class ScriptRuntime extends AbstractScriptRuntime {
}
public void stop() {
Thread.interrupted();
Thread.currentThread().interrupt();
throw new ScriptInterruptedException();
}
@Override
public void setScreenMetrics(int width, int height) {
mScreenMetrics.setScreenMetrics(width, height);
}
@Override
public ScreenMetrics getScreenMetrics() {
return mScreenMetrics;
}
public void ensureAccessibilityServiceEnabled() {

View File

@ -4,8 +4,6 @@ import android.text.TextUtils;
import com.stardust.util.ScreenMetrics;
import java.util.Arrays;
/**
* Created by Stardust on 2017/4/24.
*/
@ -35,7 +33,7 @@ public abstract class AbstractShell {
private int mTouchDevice;
private int mScreenHeight, mScreenWidth;
private ScreenMetrics mScreenMetrics;
private boolean mRoot;
@ -71,10 +69,14 @@ public abstract class AbstractShell {
}
public void SetScreenMetrics(int width, int height) {
mScreenWidth = width;
mScreenHeight = height;
mScreenMetrics.setScreenMetrics(width, height);
}
public void SetScreenMetrics(ScreenMetrics screenMetrics) {
mScreenMetrics = screenMetrics;
}
public void Touch(int x, int y) {
TouchX(x);
TouchY(y);
@ -85,13 +87,7 @@ public abstract class AbstractShell {
}
private int scaleX(int x) {
if (mScreenWidth == 0)
return x;
int screenWidth = ScreenMetrics.getScreenWidth();
if (screenWidth == mScreenWidth) {
return x;
}
return x * screenWidth / mScreenWidth;
return mScreenMetrics.scaleX(x);
}
public void TouchY(int y) {
@ -99,13 +95,8 @@ public abstract class AbstractShell {
}
private int scaleY(int y) {
if (mScreenHeight == 0)
return y;
int screenHeight = ScreenMetrics.getScreenHeight();
if (screenHeight == mScreenHeight) {
return y;
}
return y * screenHeight / mScreenHeight;
return mScreenMetrics.scaleY(y);
}
public void Tap(int x, int y) {

View File

@ -10,7 +10,7 @@ import android.widget.Button;
* Created by Stardust on 2017/5/15.
*/
public class JsButton extends Button {
public class JsButton extends android.support.v7.widget.AppCompatButton {
public JsButton(Context context) {
super(context);
}
@ -23,12 +23,11 @@ public class JsButton extends Button {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public JsButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public String text() {
return getText().toString();
}
public void text(CharSequence text) {
setText(text);
}
}

View File

@ -24,4 +24,9 @@ public class JsEditText extends android.support.v7.widget.AppCompatEditText {
public String text() {
return getText().toString();
}
public void text(CharSequence text) {
setText(text);
}
}

View File

@ -35,4 +35,8 @@ public class JsLinearLayout extends LinearLayout {
super(context, attrs, defStyleAttr, defStyleRes);
}
public View id(String id) {
return JsViewHelper.findViewByStringId(this, id);
}
}

View File

@ -11,7 +11,7 @@ import android.widget.TextView;
* Created by Stardust on 2017/5/15.
*/
public class JsTextView extends TextView {
public class JsTextView extends android.support.v7.widget.AppCompatTextView {
public JsTextView(Context context) {
super(context);
}
@ -24,12 +24,11 @@ public class JsTextView extends TextView {
super(context, attrs, defStyleAttr);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public JsTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public String text() {
return getText().toString();
}
public void text(CharSequence text) {
setText(text);
}
}

View File

@ -1,10 +1,15 @@
package com.stardust.autojs.runtime.simple_action;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.RequiresApi;
import android.util.Log;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
@ -13,11 +18,13 @@ import com.stardust.autojs.runtime.AccessibilityBridge;
import com.stardust.autojs.runtime.ScriptInterface;
import com.stardust.autojs.runtime.api.AutomatorConfig;
import com.stardust.automator.AccessibilityEventCommandHost;
import com.stardust.automator.GlobalActionAutomator;
import com.stardust.automator.UiObject;
import com.stardust.automator.simple_action.SimpleAction;
import com.stardust.automator.simple_action.ActionFactory;
import com.stardust.automator.simple_action.ActionTarget;
import com.stardust.util.DeveloperUtils;
import com.stardust.util.ScreenMetrics;
/**
* Created by Stardust on 2017/4/2.
@ -27,25 +34,9 @@ public class SimpleActionAutomator {
private static final String TAG = "SimpleActionAutomator";
@Deprecated
private static class PerformGlobalActionCommand extends AccessibilityEventCommandHost.AbstractCommand {
boolean result;
private int mGlobalAction;
PerformGlobalActionCommand(int globalAction) {
mGlobalAction = globalAction;
}
@Override
public void execute(AccessibilityService service, AccessibilityEvent event) {
result = service.performGlobalAction(mGlobalAction);
}
}
private AccessibilityBridge mAccessibilityBridge;
private AbstractScriptRuntime mScriptRuntime;
private GlobalActionAutomator mGlobalActionAutomator = new GlobalActionAutomator();
public SimpleActionAutomator(AccessibilityBridge accessibilityBridge, AbstractScriptRuntime scriptRuntime) {
mAccessibilityBridge = accessibilityBridge;
@ -168,6 +159,65 @@ public class SimpleActionAutomator {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
}
@ScriptInterface
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean gesture(long start, long duration, int[]... points) {
prepareForGesture();
return mGlobalActionAutomator.gesture(start, duration, points);
}
@RequiresApi(api = Build.VERSION_CODES.N)
public void gestureAsync(long start, long duration, int[]... points) {
prepareForGesture();
mGlobalActionAutomator.gestureAsync(start, duration, points);
}
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean gestures(GestureDescription.StrokeDescription... strokes) {
prepareForGesture();
return mGlobalActionAutomator.gestures(strokes);
}
@RequiresApi(api = Build.VERSION_CODES.N)
public void gesturesAsync(GestureDescription.StrokeDescription... strokes) {
prepareForGesture();
mGlobalActionAutomator.gesturesAsync(strokes);
}
private void prepareForGesture() {
ensureAccessibilityServiceEnabled();
mGlobalActionAutomator.setService(mAccessibilityBridge.getService());
}
@ScriptInterface
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean click(int x, int y) {
prepareForGesture();
return mGlobalActionAutomator.click(x, y);
}
@ScriptInterface
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean press(int x, int y, int delay) {
prepareForGesture();
return mGlobalActionAutomator.press(x, y, delay);
}
@ScriptInterface
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean longClick(int x, int y) {
prepareForGesture();
return mGlobalActionAutomator.longClick(x, y);
}
@ScriptInterface
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean swipe(int x1, int y1, int x2, int y2, int delay) {
prepareForGesture();
return mGlobalActionAutomator.swipe(x1, y1, x2, y2, delay);
}
private boolean performGlobalAction(final int action) {
ensureAccessibilityServiceEnabled();
AccessibilityService service = mAccessibilityBridge.getService();
@ -205,4 +255,9 @@ public class SimpleActionAutomator {
private boolean isRunningPackageSelf() {
return DeveloperUtils.isSelfPackage(mAccessibilityBridge.getInfoProvider().getLatestPackage());
}
public void setScreenMetrics(ScreenMetrics metrics) {
mGlobalActionAutomator.setScreenMetrics(metrics);
}
}

View File

@ -0,0 +1,175 @@
package com.stardust.automator;
import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.GestureDescription;
import android.content.Context;
import android.graphics.Path;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.RequiresApi;
import android.view.ViewConfiguration;
import com.stardust.concurrent.VolatileBox;
import com.stardust.util.ScreenMetrics;
/**
* Created by Stardust on 2017/5/16.
*/
public class GlobalActionAutomator {
private AccessibilityService mService;
private ScreenMetrics mScreenMetrics;
public void setService(AccessibilityService service) {
mService = service;
}
public void setScreenMetrics(ScreenMetrics screenMetrics) {
mScreenMetrics = screenMetrics;
}
public boolean back() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_BACK);
}
public boolean home() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_HOME);
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public boolean powerDialog() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
}
private boolean performGlobalAction(int globalAction) {
if (mService == null)
return false;
return mService.performGlobalAction(globalAction);
}
public boolean notifications() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
}
public boolean quickSettings() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
}
public boolean recents() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
}
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean splitScreen() {
return performGlobalAction(AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
}
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean gesture(long start, long duration, int[]... points) {
Path path = pointsToPath(points);
return gestures(new GestureDescription.StrokeDescription(path, start, duration));
}
private Path pointsToPath(int[][] points) {
Path path = new Path();
path.moveTo(scaleX(points[0][0]), scaleY(points[0][1]));
for (int i = 1; i < points.length; i++) {
int[] point = points[i];
path.lineTo(scaleX(point[0]), scaleY(point[1]));
}
return path;
}
@RequiresApi(api = Build.VERSION_CODES.N)
public void gestureAsync(long start, long duration, int[]... points) {
Path path = pointsToPath(points);
gesturesAsync(new GestureDescription.StrokeDescription(path, start, duration));
}
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean gestures(GestureDescription.StrokeDescription... strokes) {
if (mService == null)
return false;
GestureDescription.Builder builder = new GestureDescription.Builder();
for (GestureDescription.StrokeDescription stroke : strokes) {
builder.addStroke(stroke);
}
prepareLooperIfNeeded();
final VolatileBox<Boolean> result = new VolatileBox<>(false);
Handler handler = new Handler(Looper.myLooper());
mService.dispatchGesture(builder.build(), new AccessibilityService.GestureResultCallback() {
@Override
public void onCompleted(GestureDescription gestureDescription) {
result.set(true);
quitLoop();
}
@Override
public void onCancelled(GestureDescription gestureDescription) {
result.set(false);
quitLoop();
}
}, handler);
Looper.loop();
return result.get();
}
@RequiresApi(api = Build.VERSION_CODES.N)
public void gesturesAsync(GestureDescription.StrokeDescription... strokes) {
if (mService == null)
return;
GestureDescription.Builder builder = new GestureDescription.Builder();
for (GestureDescription.StrokeDescription stroke : strokes) {
builder.addStroke(stroke);
}
mService.dispatchGesture(builder.build(), null, null);
}
private void quitLoop() {
Looper looper = Looper.myLooper();
if (looper != null) {
looper.quitSafely();
}
}
private void prepareLooperIfNeeded() {
if (Looper.myLooper() == null) {
Looper.prepare();
}
}
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean click(int x, int y) {
return press(x, y, ViewConfiguration.getTapTimeout() + 50);
}
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean press(int x, int y, int delay) {
return gesture(0, delay, new int[]{x, y});
}
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean longClick(int x, int y) {
return gesture(0, ViewConfiguration.getLongPressTimeout() + 200, new int[]{x, y});
}
private int scaleX(int x) {
if (mScreenMetrics == null)
return x;
return mScreenMetrics.scaleX(x);
}
private int scaleY(int y) {
if (mScreenMetrics == null)
return y;
return mScreenMetrics.scaleY(y);
}
@RequiresApi(api = Build.VERSION_CODES.N)
public boolean swipe(int x1, int y1, int x2, int y2, int delay) {
return gesture(0, delay, new int[]{x1, y1}, new int[]{x2, y2});
}
}

View File

@ -14,6 +14,8 @@ import java.util.Set;
public interface AccessibilityDelegate {
Set<Integer> ALL_EVENT_TYPES = null;
boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event);
/**

View File

@ -30,41 +30,40 @@ public class AccessibilityInfoProvider implements AccessibilityDelegate {
mPackageManager = packageManager;
}
public synchronized String getLatestPackage() {
public String getLatestPackage() {
return mLatestPackage;
}
public synchronized String getLatestActivity() {
public String getLatestActivity() {
return mLatestActivity;
}
@Override
public boolean onAccessibilityEvent(AccessibilityService service, AccessibilityEvent event) {
AccessibilityNodeInfo root = service.getRootInActiveWindow();
if (root != null)
setLatestComponent(root.getPackageName(), event.getClassName());
else
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
setLatestComponent(event.getPackageName(), event.getClassName());
}
return false;
}
@Override
public Set<Integer> getEventTypes() {
return null;
return ALL_EVENT_TYPES;
}
private synchronized void setLatestComponent(CharSequence latestPackage, CharSequence latestClass) {
if (latestPackage == null)
private void setLatestComponent(CharSequence latestPackage, CharSequence latestClass) {
if (latestPackage == null || latestClass == null)
return;
mLatestPackage = latestPackage.toString();
if (latestClass == null)
String latestPackageStr = latestPackage.toString();
String latestClassStr = latestClass.toString();
if (latestClassStr.startsWith("android.view.") || latestClassStr.startsWith("android.widget."))
return;
try {
ComponentName componentName = new ComponentName(latestPackage.toString(), latestClass.toString());
ActivityInfo activityInfo = mPackageManager.getActivityInfo(componentName, 0);
mLatestActivity = activityInfo.name;
ComponentName componentName = new ComponentName(latestPackageStr, latestClassStr);
mLatestActivity = mPackageManager.getActivityInfo(componentName, 0).name;
} catch (PackageManager.NameNotFoundException ignored) {
return;
}
mLatestPackage = latestPackage.toString();
}
}

View File

@ -2,13 +2,23 @@ package com.stardust.view.accessibility;
import android.view.accessibility.AccessibilityNodeInfo;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Created by Stardust on 2017/5/2.
*/
public abstract class AccessibilityService extends android.accessibilityservice.AccessibilityService {
public interface NotificationCallback {
void onNotification();
}
private CopyOnWriteArrayList<NotificationCallback> mNotificationCallbacks = new CopyOnWriteArrayList<>();
@Override
public AccessibilityNodeInfo getRootInActiveWindow() {
try {
return super.getRootInActiveWindow();
@ -16,4 +26,5 @@ public abstract class AccessibilityService extends android.accessibilityservice.
return null;
}
}
}

View File

@ -1,6 +1,5 @@
package com.stardust.autojs.runtime.api.internal;
package com.stardust.concurrent;
import com.stardust.autojs.runtime.ScriptInterruptedException;
/**
* Created by Stardust on 2017/5/8.
@ -38,7 +37,7 @@ public class VolatileBox<T> {
try {
this.wait();
} catch (InterruptedException e) {
throw new ScriptInterruptedException();
throw new RuntimeException(e);
}
}
return mValue;

View File

@ -3,33 +3,60 @@ package com.stardust.util;
import android.app.Activity;
import android.util.DisplayMetrics;
import com.stardust.pio.PFile;
/**
* Created by Stardust on 2017/4/26.
*/
public class ScreenMetrics {
private static int screenHeight;
private static int screenWidth;
private static int deviceScreenHeight;
private static int deviceScreenWidth;
private static boolean initialized = false;
public static void initIfNeeded(Activity activity) {
if (!initialized) {
DisplayMetrics metrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(metrics);
screenHeight = metrics.heightPixels;
screenWidth = metrics.widthPixels;
deviceScreenHeight = metrics.heightPixels;
deviceScreenWidth = metrics.widthPixels;
initialized = true;
}
}
public static int getScreenHeight() {
return screenHeight;
public static int getDeviceScreenHeight() {
return deviceScreenHeight;
}
public static int getScreenWidth() {
return screenWidth;
public static int getDeviceScreenWidth() {
return deviceScreenWidth;
}
private int mScreenWidth;
private int mScreenHeight;
public void setScreenWidth(int screenWidth) {
mScreenWidth = screenWidth;
}
public void setScreenHeight(int screenHeight) {
mScreenHeight = screenHeight;
}
public int scaleX(int x) {
if (mScreenWidth == 0 || !initialized)
return x;
return x * deviceScreenWidth / mScreenWidth;
}
public int scaleY(int y) {
if (mScreenHeight == 0 || !initialized)
return y;
return y * deviceScreenHeight / mScreenHeight;
}
public void setScreenMetrics(int width, int height) {
mScreenWidth = width;
mScreenHeight = height;
}
}