├── .gitignore ├── README.MD ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro ├── release │ ├── output.json │ ├── 自动点赞助手1.2.apk │ ├── 自动点赞助手1.2.apk.zip │ ├── 自动点赞助手1.3.apk │ ├── 自动点赞助手1.4.apk │ ├── 麻辣鸡触控助手.apk │ ├── 麻辣鸡触控助手.zip │ ├── 麻辣鸡触控助手1.1.apk │ └── 麻辣鸡触控助手1.1.zip └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── zhang │ │ └── autotouch │ │ ├── MainActivity.java │ │ ├── TouchEventManager.java │ │ ├── adapter │ │ └── TouchPointAdapter.java │ │ ├── bean │ │ ├── TouchEvent.java │ │ └── TouchPoint.java │ │ ├── dialog │ │ ├── AddPointDialog.java │ │ ├── BaseServiceDialog.java │ │ ├── MenuDialog.java │ │ └── RecordDialog.java │ │ ├── fw_permission │ │ ├── Api23CompatImpl.java │ │ ├── BelowApi23CompatImpl.java │ │ ├── FloatWinPermissionCompat.java │ │ ├── HuaweiCompatImpl.java │ │ ├── MeizuCompatImpl.java │ │ ├── MiuiCompatImpl.java │ │ ├── QihooCompatImpl.java │ │ └── RomUtils.java │ │ ├── listener │ │ └── ScreenListener.java │ │ ├── service │ │ ├── AutoTouchService.java │ │ └── FloatingService.java │ │ └── utils │ │ ├── AccessibilityNodeInfoUtil.java │ │ ├── AccessibilityUtil.java │ │ ├── CopyPasteUtil.java │ │ ├── DensityUtil.java │ │ ├── DialogUtils.java │ │ ├── GsonUtils.java │ │ ├── KeyboardDownUtil.java │ │ ├── LoopArrayList.java │ │ ├── SpUtils.java │ │ ├── ToastUtil.java │ │ └── WindowUtils.java │ └── res │ ├── drawable │ ├── shape_bg_touch.xml │ ├── shape_bt_home.xml │ └── shape_dialog_menu_bg.xml │ ├── layout │ ├── activity_main.xml │ ├── dialog_add_point.xml │ ├── dialog_menu.xml │ ├── dialog_record.xml │ ├── item_touch_point.xml │ ├── layout_window.xml │ └── window_touch_point.xml │ ├── mipmap │ └── ic_launcher.png │ ├── values │ ├── colors.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── accessibility_config.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | .idea 5 | /.idea/caches 6 | /.idea/libraries 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | /.idea/navEditor.xml 10 | /.idea/assetWizardSettings.xml 11 | .DS_Store 12 | /build 13 | /captures 14 | .externalNativeBuild 15 | .cxx 16 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # 自动点击屏幕助手 2 | 3 | # auto to touch one point on postdelay time 4 | ## auto click the scan 5 | 6 | 自动点击屏幕 7 | 自动点赞助手 8 | 自动点赞apk 9 | 10 | ## 无障碍辅助 11 | ## 桌面悬浮框 12 | 13 | 基于https://github.com/superzhangjh/AutoTouch的1.1版本做的优化 -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.2" 6 | defaultConfig { 7 | applicationId "com.zhang.autotouch" 8 | minSdkVersion 24 9 | targetSdkVersion 29 10 | versionCode 2 11 | versionName "1.4" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | 20 | android.applicationVariants.all { 21 | variant -> 22 | variant.outputs.all { 23 | //在这里修改apk文件名,引号内的字符串都可以随便定义 24 | outputFileName = "自动点赞助手${variant.versionName}.apk" 25 | } 26 | } 27 | 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(dir: 'libs', include: ['*.jar']) 32 | implementation 'androidx.appcompat:appcompat:1.1.0' 33 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 34 | implementation 'org.greenrobot:eventbus:3.1.1' 35 | implementation 'me.jessyan:autosize:1.1.2' 36 | implementation 'com.google.code.gson:gson:2.8.5' 37 | implementation 'androidx.recyclerview:recyclerview:1.1.0' 38 | } 39 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/release/output.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damonlear/AutoTouch/8da4460070fa69eaa4bb70fce9b13ba1e5f55b77/app/release/output.json -------------------------------------------------------------------------------- /app/release/自动点赞助手1.2.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damonlear/AutoTouch/8da4460070fa69eaa4bb70fce9b13ba1e5f55b77/app/release/自动点赞助手1.2.apk -------------------------------------------------------------------------------- /app/release/自动点赞助手1.2.apk.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damonlear/AutoTouch/8da4460070fa69eaa4bb70fce9b13ba1e5f55b77/app/release/自动点赞助手1.2.apk.zip -------------------------------------------------------------------------------- /app/release/自动点赞助手1.3.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damonlear/AutoTouch/8da4460070fa69eaa4bb70fce9b13ba1e5f55b77/app/release/自动点赞助手1.3.apk -------------------------------------------------------------------------------- /app/release/自动点赞助手1.4.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damonlear/AutoTouch/8da4460070fa69eaa4bb70fce9b13ba1e5f55b77/app/release/自动点赞助手1.4.apk -------------------------------------------------------------------------------- /app/release/麻辣鸡触控助手.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damonlear/AutoTouch/8da4460070fa69eaa4bb70fce9b13ba1e5f55b77/app/release/麻辣鸡触控助手.apk -------------------------------------------------------------------------------- /app/release/麻辣鸡触控助手.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damonlear/AutoTouch/8da4460070fa69eaa4bb70fce9b13ba1e5f55b77/app/release/麻辣鸡触控助手.zip -------------------------------------------------------------------------------- /app/release/麻辣鸡触控助手1.1.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damonlear/AutoTouch/8da4460070fa69eaa4bb70fce9b13ba1e5f55b77/app/release/麻辣鸡触控助手1.1.apk -------------------------------------------------------------------------------- /app/release/麻辣鸡触控助手1.1.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/damonlear/AutoTouch/8da4460070fa69eaa4bb70fce9b13ba1e5f55b77/app/release/麻辣鸡触控助手1.1.zip -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 22 | 23 | 26 | 27 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 49 | 50 | 53 | 54 | 55 | 56 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch; 2 | 3 | import androidx.appcompat.app.AlertDialog; 4 | import androidx.appcompat.app.AppCompatActivity; 5 | 6 | import android.annotation.SuppressLint; 7 | import android.content.DialogInterface; 8 | import android.content.Intent; 9 | import android.os.Bundle; 10 | import android.util.Log; 11 | import android.view.View; 12 | import android.widget.TextView; 13 | import android.widget.Toast; 14 | 15 | import com.zhang.autotouch.fw_permission.FloatWinPermissionCompat; 16 | import com.zhang.autotouch.service.AutoTouchService; 17 | import com.zhang.autotouch.service.FloatingService; 18 | import com.zhang.autotouch.utils.AccessibilityUtil; 19 | import com.zhang.autotouch.utils.ToastUtil; 20 | 21 | import java.text.SimpleDateFormat; 22 | import java.util.Date; 23 | 24 | 25 | @SuppressLint("SetTextI18n") 26 | public class MainActivity extends AppCompatActivity { 27 | 28 | private TextView tvStart; 29 | private final String STRING_START = "开始"; 30 | private final String STRING_ACCESS = "无障碍服务"; 31 | private final String STRING_ALERT = "悬浮窗权限"; 32 | 33 | 34 | @Override 35 | protected void onCreate(Bundle savedInstanceState) { 36 | super.onCreate(savedInstanceState); 37 | setContentView(R.layout.activity_main); 38 | tvStart = findViewById(R.id.tv_start); 39 | tvStart.setOnClickListener(new View.OnClickListener() { 40 | @Override 41 | public void onClick(View v) { 42 | switch (tvStart.getText().toString()) { 43 | case STRING_START: 44 | ToastUtil.show(getString(R.string.app_name) + "已启用"); 45 | startService(new Intent(MainActivity.this, FloatingService.class)); 46 | moveTaskToBack(true); 47 | break; 48 | case STRING_ALERT: 49 | requestPermissionAndShow(); 50 | break; 51 | case STRING_ACCESS: 52 | requestAcccessibility(); 53 | break; 54 | default: 55 | break; 56 | } 57 | } 58 | }); 59 | ToastUtil.init(this); 60 | } 61 | 62 | @Override 63 | protected void onResume() { 64 | super.onResume(); 65 | checkState(); 66 | } 67 | 68 | private boolean isGuoQi() { 69 | try { 70 | SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd"); 71 | Date date = formatter.parse("20211001"); 72 | return (date.getTime() - System.currentTimeMillis()) < 0; 73 | }catch (Exception e){ 74 | e.printStackTrace(); 75 | return true; 76 | } 77 | } 78 | 79 | private void checkState() { 80 | boolean hasAccessibility = AccessibilityUtil.isSettingOpen(AutoTouchService.class, MainActivity.this); 81 | boolean hasWinPermission = FloatWinPermissionCompat.getInstance().check(this); 82 | if (hasAccessibility) { 83 | if (hasWinPermission) { 84 | tvStart.setText(STRING_START); 85 | } else { 86 | tvStart.setText(STRING_ALERT); 87 | } 88 | } else { 89 | tvStart.setText(STRING_ACCESS); 90 | } 91 | } 92 | 93 | private void requestAcccessibility() { 94 | new AlertDialog.Builder(this).setTitle("无障碍服务未开启") 95 | .setMessage("你的手机没有开启无障碍服务," + getString(R.string.app_name) + "将无法正常使用") 96 | .setPositiveButton("去开启", new DialogInterface.OnClickListener() { 97 | @Override 98 | public void onClick(DialogInterface dialog, int which) { 99 | // 显示授权界面 100 | try { 101 | AccessibilityUtil.jumpToSetting(MainActivity.this); 102 | } catch (Exception e) { 103 | e.printStackTrace(); 104 | } 105 | } 106 | }) 107 | .setNegativeButton("取消", null).show(); 108 | } 109 | 110 | /** 111 | * 开启悬浮窗权限 112 | */ 113 | private void requestPermissionAndShow() { 114 | new AlertDialog.Builder(this).setTitle("悬浮窗权限未开启") 115 | .setMessage(getString(R.string.app_name) + "获得悬浮窗权限,才能正常使用应用") 116 | .setPositiveButton("去开启", new DialogInterface.OnClickListener() { 117 | @Override 118 | public void onClick(DialogInterface dialog, int which) { 119 | // 显示授权界面 120 | try { 121 | FloatWinPermissionCompat.getInstance().apply(MainActivity.this); 122 | } catch (Exception e) { 123 | e.printStackTrace(); 124 | } 125 | } 126 | }) 127 | .setNegativeButton("取消", null).show(); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/TouchEventManager.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch; 2 | 3 | import android.widget.Button; 4 | 5 | import com.zhang.autotouch.bean.TouchEvent; 6 | 7 | import org.greenrobot.eventbus.EventBus; 8 | import org.greenrobot.eventbus.Subscribe; 9 | import org.greenrobot.eventbus.ThreadMode; 10 | 11 | public class TouchEventManager { 12 | 13 | private static TouchEventManager touchEventManager; 14 | private int touchAction; 15 | 16 | public static TouchEventManager getInstance() { 17 | if (touchEventManager == null) { 18 | synchronized (TouchEventManager.class) { 19 | if (touchEventManager == null) { 20 | touchEventManager = new TouchEventManager(); 21 | } 22 | } 23 | } 24 | return touchEventManager; 25 | } 26 | 27 | private TouchEventManager() { } 28 | 29 | public void setTouchAction(int touchAction) { 30 | this.touchAction = touchAction; 31 | } 32 | 33 | public int getTouchAction() { 34 | return touchAction; 35 | } 36 | 37 | /** 38 | * @return 正在触控 39 | */ 40 | public boolean isTouching() { 41 | return touchAction == TouchEvent.ACTION_START || touchAction == TouchEvent.ACTION_CONTINUE; 42 | } 43 | 44 | public boolean isPaused() { 45 | return touchAction == TouchEvent.ACTION_PAUSE; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/adapter/TouchPointAdapter.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.adapter; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.text.TextUtils; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.Button; 9 | import android.widget.TextView; 10 | 11 | import androidx.annotation.NonNull; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | 14 | import com.zhang.autotouch.R; 15 | import com.zhang.autotouch.bean.TouchPoint; 16 | 17 | import java.util.List; 18 | 19 | public class TouchPointAdapter extends RecyclerView.Adapter implements View.OnClickListener { 20 | 21 | private List touchPointList; 22 | private OnItemClickListener onItemClickListener; 23 | // private int touchPosition = -1; 24 | 25 | public TouchPointAdapter() { 26 | } 27 | 28 | public void setTouchPointList(List touchPointList) { 29 | this.touchPointList = touchPointList; 30 | notifyDataSetChanged(); 31 | } 32 | 33 | @NonNull 34 | @Override 35 | public TouchPointHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 36 | View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_touch_point, parent, false); 37 | view.setOnClickListener(this); 38 | return new TouchPointHolder(view); 39 | } 40 | 41 | @SuppressLint("SetTextI18n") 42 | @Override 43 | public void onBindViewHolder(@NonNull TouchPointHolder holder, int position) { 44 | holder.itemView.setTag(position); 45 | TouchPoint touchPoint = getItem(position); 46 | if (TextUtils.isEmpty(touchPoint.getText())) { 47 | holder.tvName.setText("点赞:" + touchPoint.getName()); 48 | }else { 49 | holder.tvName.setText("评论:" + touchPoint.getName()); 50 | } 51 | holder.tvOffset.setText("间隔(" + touchPoint.getDelay() + "s)"); 52 | // holder.btStop.setVisibility(touchPosition == position ? View.VISIBLE : View.INVISIBLE); 53 | } 54 | 55 | @Override 56 | public int getItemCount() { 57 | return touchPointList == null ? 0 : touchPointList.size(); 58 | } 59 | 60 | public TouchPoint getItem(int position) { 61 | return touchPointList.get(position); 62 | } 63 | 64 | @Override 65 | public void onClick(View v) { 66 | if (onItemClickListener != null) { 67 | int postion = (int) v.getTag(); 68 | TouchPoint touchPoint = getItem(postion); 69 | onItemClickListener.onItemClick(v, postion, touchPoint); 70 | } 71 | } 72 | 73 | public void setOnItemClickListener(OnItemClickListener onItemClickListener) { 74 | this.onItemClickListener = onItemClickListener; 75 | } 76 | 77 | public interface OnItemClickListener { 78 | void onItemClick(View view, int position, TouchPoint touchPoint); 79 | } 80 | 81 | public static class TouchPointHolder extends RecyclerView.ViewHolder { 82 | 83 | TextView tvName, tvOffset; 84 | Button btStop; 85 | 86 | public TouchPointHolder(@NonNull View itemView) { 87 | super(itemView); 88 | tvName = itemView.findViewById(R.id.tv_name); 89 | tvOffset = itemView.findViewById(R.id.tv_offset); 90 | btStop = itemView.findViewById(R.id.bt_stop); 91 | } 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/bean/TouchEvent.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.bean; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.zhang.autotouch.utils.GsonUtils; 6 | 7 | import org.greenrobot.eventbus.EventBus; 8 | 9 | public class TouchEvent { 10 | 11 | public static final int ACTION_START = 1; 12 | public static final int ACTION_PAUSE = 2; 13 | public static final int ACTION_CONTINUE = 3; 14 | public static final int ACTION_STOP = 4; 15 | 16 | private int action; 17 | private TouchPoint touchPoint; 18 | 19 | private TouchEvent(int action) { 20 | this(action, null); 21 | } 22 | 23 | private TouchEvent(int action, TouchPoint touchPoint) { 24 | this.action = action; 25 | this.touchPoint = touchPoint; 26 | } 27 | 28 | public void setAction(int action) { 29 | this.action = action; 30 | } 31 | 32 | public int getAction() { 33 | return action; 34 | } 35 | 36 | public TouchPoint getTouchPoint() { 37 | return touchPoint; 38 | } 39 | 40 | public static void postStartAction(TouchPoint touchPoint) { 41 | postAction(new TouchEvent(ACTION_START, touchPoint)); 42 | } 43 | 44 | public static void postPauseAction() { 45 | postAction(new TouchEvent(ACTION_PAUSE)); 46 | } 47 | 48 | public static void postContinueAction() { 49 | postAction(new TouchEvent(ACTION_CONTINUE)); 50 | } 51 | 52 | public static void postStopAction() { 53 | postAction(new TouchEvent(ACTION_STOP)); 54 | } 55 | 56 | private static void postAction(TouchEvent touchEvent) { 57 | EventBus.getDefault().post(touchEvent); 58 | } 59 | 60 | @NonNull 61 | @Override 62 | public String toString() { 63 | return "action=" + action + " touchPoint=" + (touchPoint == null ? "null" : GsonUtils.beanToJson(touchPoint)); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/bean/TouchPoint.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.bean; 2 | 3 | public class TouchPoint { 4 | private String name; 5 | private int x; 6 | private int y; 7 | private int delay; 8 | private String text; 9 | 10 | public TouchPoint(String name, int x, int y, int delay) { 11 | this.name = name; 12 | this.x = x; 13 | this.y = y; 14 | this.delay = delay; 15 | } 16 | 17 | public TouchPoint(String name, int x, int y, int delay, String text) { 18 | this.name = name; 19 | this.x = x; 20 | this.y = y; 21 | this.delay = delay; 22 | this.text = text; 23 | } 24 | 25 | public String getName() { 26 | return name; 27 | } 28 | 29 | public int getX() { 30 | return x; 31 | } 32 | 33 | public int getY() { 34 | return y; 35 | } 36 | 37 | public int getDelay() { 38 | return delay; 39 | } 40 | 41 | public String getText() { 42 | return text; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/dialog/AddPointDialog.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.dialog; 2 | 3 | import android.content.Context; 4 | import android.text.TextUtils; 5 | import android.view.MotionEvent; 6 | import android.view.View; 7 | import android.view.WindowManager; 8 | import android.widget.CheckBox; 9 | import android.widget.EditText; 10 | import android.widget.RadioButton; 11 | import android.widget.RadioGroup; 12 | import android.widget.TextView; 13 | 14 | import androidx.annotation.NonNull; 15 | import androidx.constraintlayout.widget.Group; 16 | 17 | import com.zhang.autotouch.R; 18 | import com.zhang.autotouch.bean.TouchPoint; 19 | import com.zhang.autotouch.utils.SpUtils; 20 | import com.zhang.autotouch.utils.ToastUtil; 21 | 22 | import java.text.SimpleDateFormat; 23 | import java.util.Date; 24 | import java.util.Locale; 25 | 26 | 27 | public class AddPointDialog extends BaseServiceDialog implements View.OnClickListener, RadioGroup.OnCheckedChangeListener { 28 | 29 | private EditText etName; 30 | private EditText etTime; 31 | private EditText etText; 32 | private Group groupInput; 33 | private TextView tvHint; 34 | private int x; 35 | private int y; 36 | private RadioButton radiobutton_2; 37 | 38 | public AddPointDialog(@NonNull Context context) { 39 | super(context); 40 | } 41 | 42 | @Override 43 | protected int getLayoutId() { 44 | return R.layout.dialog_add_point; 45 | } 46 | 47 | @Override 48 | protected int getWidth() { 49 | return WindowManager.LayoutParams.MATCH_PARENT; 50 | } 51 | 52 | @Override 53 | protected int getHeight() { 54 | return WindowManager.LayoutParams.MATCH_PARENT; 55 | } 56 | 57 | @Override 58 | protected void onInited() { 59 | etName = findViewById(R.id.et_name); 60 | SimpleDateFormat f = new SimpleDateFormat( "yyyy年MM月dd日HH点mm分ss秒", Locale.getDefault()); 61 | etName.setText(f.format(new Date())); 62 | etTime = findViewById(R.id.et_time); 63 | etText = findViewById(R.id.et_text); 64 | etText.setVisibility(View.INVISIBLE); 65 | RadioGroup radioGroup = findViewById(R.id.radio_group); 66 | RadioButton radiobutton_1 = findViewById(R.id.radiobutton_1); 67 | radiobutton_2 = findViewById(R.id.radiobutton_2); 68 | radiobutton_2.setChecked(false); 69 | radiobutton_1.setChecked(true); 70 | radioGroup.setOnCheckedChangeListener(this); 71 | etTime.setText("1"); 72 | groupInput = findViewById(R.id.gl_input); 73 | tvHint = findViewById(R.id.tv_hint); 74 | findViewById(R.id.bt_commit).setOnClickListener(this); 75 | findViewById(R.id.bt_cancel).setOnClickListener(this); 76 | } 77 | 78 | @Override 79 | public boolean onTouchEvent(@NonNull MotionEvent event) { 80 | if (event.getAction() == MotionEvent.ACTION_UP) { 81 | x = (int) event.getRawX(); 82 | y = (int) event.getRawY(); 83 | tvHint.setVisibility(View.GONE); 84 | groupInput.setVisibility(View.VISIBLE); 85 | } 86 | return super.onTouchEvent(event); 87 | } 88 | 89 | @Override 90 | public void onClick(View v) { 91 | switch (v.getId()) { 92 | case R.id.bt_commit: 93 | String name = null; 94 | int second = 0; 95 | try { 96 | name = etName.getText().toString().trim(); 97 | second = Integer.parseInt(etTime.getText().toString().trim()); 98 | } catch (Exception e) { 99 | e.printStackTrace(); 100 | } 101 | if (TextUtils.isEmpty(name) || second <= 0) { 102 | ToastUtil.show("名字或秒数错误"); 103 | return; 104 | } 105 | String text = null; 106 | if (radiobutton_2.isChecked()) { 107 | text = etText.getText().toString().trim(); 108 | if (TextUtils.isEmpty(text)) { 109 | ToastUtil.show("评论模式必须输入评论文字"); 110 | return; 111 | } 112 | } 113 | 114 | TouchPoint touchPoint = new TouchPoint(name, x, y, second, text); 115 | SpUtils.addTouchPoint(getContext(), touchPoint); 116 | dismiss(); 117 | break; 118 | case R.id.bt_cancel: 119 | dismiss(); 120 | break; 121 | } 122 | } 123 | 124 | @Override 125 | public void onCheckedChanged(RadioGroup group, int checkedId) { 126 | switch (checkedId) { 127 | case R.id.radiobutton_1: 128 | etText.setText(""); 129 | etText.setVisibility(View.INVISIBLE); 130 | break; 131 | case R.id.radiobutton_2: 132 | etText.setVisibility(View.VISIBLE); 133 | break; 134 | default: 135 | break; 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/dialog/BaseServiceDialog.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.dialog; 2 | 3 | import android.app.Dialog; 4 | import android.content.Context; 5 | import android.os.Build; 6 | import android.view.Window; 7 | import android.view.WindowManager; 8 | import androidx.annotation.NonNull; 9 | 10 | import com.zhang.autotouch.R; 11 | 12 | /** 13 | * 在Service中可使用的dialog 14 | */ 15 | public abstract class BaseServiceDialog extends Dialog { 16 | 17 | public BaseServiceDialog(@NonNull Context context) { 18 | super(context, R.style.NoTitleDialog); 19 | setContentView(getLayoutId()); 20 | onInited(); 21 | } 22 | 23 | @Override 24 | public void show() { 25 | Window window = getWindow(); 26 | if (window != null) { 27 | if (Build.VERSION.SDK_INT >= 26) { 28 | window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); 29 | } else { 30 | window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 31 | } 32 | WindowManager.LayoutParams wmParams = window.getAttributes(); 33 | wmParams.width = getWidth(); 34 | wmParams.height = getHeight(); 35 | window.setAttributes(wmParams); 36 | } 37 | super.show(); 38 | } 39 | 40 | protected abstract int getLayoutId(); 41 | 42 | protected abstract int getWidth(); 43 | 44 | protected abstract int getHeight(); 45 | 46 | protected abstract void onInited(); 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/dialog/MenuDialog.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.dialog; 2 | 3 | import android.content.Context; 4 | import android.content.DialogInterface; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.view.WindowManager; 8 | import android.widget.Button; 9 | 10 | import androidx.annotation.NonNull; 11 | import androidx.recyclerview.widget.LinearLayoutManager; 12 | import androidx.recyclerview.widget.RecyclerView; 13 | 14 | import com.zhang.autotouch.R; 15 | import com.zhang.autotouch.TouchEventManager; 16 | import com.zhang.autotouch.adapter.TouchPointAdapter; 17 | import com.zhang.autotouch.bean.TouchEvent; 18 | import com.zhang.autotouch.bean.TouchPoint; 19 | import com.zhang.autotouch.utils.DensityUtil; 20 | import com.zhang.autotouch.utils.DialogUtils; 21 | import com.zhang.autotouch.utils.GsonUtils; 22 | import com.zhang.autotouch.utils.SpUtils; 23 | import com.zhang.autotouch.utils.ToastUtil; 24 | 25 | import java.util.Collections; 26 | import java.util.List; 27 | 28 | public class MenuDialog extends BaseServiceDialog implements View.OnClickListener { 29 | 30 | private Button btStop; 31 | private RecyclerView rvPoints; 32 | 33 | private AddPointDialog addPointDialog; 34 | private Listener listener; 35 | private TouchPointAdapter touchPointAdapter; 36 | private RecordDialog recordDialog; 37 | 38 | public MenuDialog(@NonNull Context context) { 39 | super(context); 40 | ToastUtil.init(context); 41 | } 42 | 43 | @Override 44 | protected int getLayoutId() { 45 | return R.layout.dialog_menu; 46 | } 47 | 48 | @Override 49 | protected int getWidth() { 50 | return DensityUtil.dip2px(getContext(), 350); 51 | } 52 | 53 | @Override 54 | protected int getHeight() { 55 | return WindowManager.LayoutParams.WRAP_CONTENT; 56 | } 57 | 58 | @Override 59 | protected void onInited() { 60 | setCanceledOnTouchOutside(true); 61 | findViewById(R.id.bt_exit).setOnClickListener(this); 62 | findViewById(R.id.bt_add).setOnClickListener(this); 63 | findViewById(R.id.bt_clear).setOnClickListener(this); 64 | findViewById(R.id.bt_record).setOnClickListener(this); 65 | findViewById(R.id.bt_list_start).setOnClickListener(this); 66 | btStop = findViewById(R.id.bt_stop); 67 | btStop.setOnClickListener(this); 68 | rvPoints = findViewById(R.id.rv); 69 | touchPointAdapter = new TouchPointAdapter(); 70 | touchPointAdapter.setOnItemClickListener(new TouchPointAdapter.OnItemClickListener() { 71 | @Override 72 | public void onItemClick(View view, int position, TouchPoint touchPoint) { 73 | btStop.setVisibility(View.VISIBLE); 74 | dismiss(); 75 | TouchEvent.postStartAction(touchPoint); 76 | ToastUtil.show("已开启触控点:" + touchPoint.getName()); 77 | } 78 | }); 79 | rvPoints.setLayoutManager(new LinearLayoutManager(getContext())); 80 | rvPoints.setAdapter(touchPointAdapter); 81 | setOnDismissListener(new OnDismissListener() { 82 | @Override 83 | public void onDismiss(DialogInterface dialog) { 84 | if (TouchEventManager.getInstance().isPaused()) { 85 | TouchEvent.postContinueAction(); 86 | } 87 | } 88 | }); 89 | } 90 | 91 | @Override 92 | protected void onStart() { 93 | super.onStart(); 94 | Log.d("啊实打实", "onStart"); 95 | //如果正在触控,则暂停 96 | onClick(btStop); 97 | if (touchPointAdapter != null) { 98 | List touchPoints = SpUtils.getTouchPoints(getContext()); 99 | Collections.reverse(touchPoints); 100 | Log.d("啊实打实", GsonUtils.beanToJson(touchPoints)); 101 | touchPointAdapter.setTouchPointList(touchPoints); 102 | } 103 | } 104 | 105 | @Override 106 | public void onClick(View v) { 107 | switch (v.getId()) { 108 | case R.id.bt_add: 109 | DialogUtils.dismiss(addPointDialog); 110 | addPointDialog = new AddPointDialog(getContext()); 111 | addPointDialog.setOnDismissListener(new OnDismissListener() { 112 | @Override 113 | public void onDismiss(DialogInterface dialog) { 114 | MenuDialog.this.show(); 115 | } 116 | }); 117 | addPointDialog.show(); 118 | dismiss(); 119 | break; 120 | case R.id.bt_record: 121 | ToastUtil.show("有BUG暂不支持"); 122 | dismiss(); 123 | break; 124 | case R.id.bt_clear: 125 | onClick(btStop); 126 | SpUtils.clear(getContext()); 127 | ToastUtil.show("已清空"); 128 | 129 | DialogUtils.dismiss(addPointDialog); 130 | dismiss(); 131 | break; 132 | case R.id.bt_stop: 133 | btStop.setVisibility(View.GONE); 134 | TouchEvent.postStopAction(); 135 | ToastUtil.show("已停止触控"); 136 | break; 137 | case R.id.bt_exit: 138 | TouchEvent.postStopAction(); 139 | if (listener != null) { 140 | listener.onExitService(); 141 | } 142 | break; 143 | case R.id.bt_list_start: 144 | if (SpUtils.getTouchPoints(getContext()).size() > 0) { 145 | btStop.setVisibility(View.VISIBLE); 146 | dismiss(); 147 | TouchEvent.postStartAction(null); 148 | } else { 149 | ToastUtil.show("列表为空"); 150 | } 151 | break; 152 | default: 153 | break; 154 | 155 | } 156 | } 157 | 158 | public void setListener(Listener listener) { 159 | this.listener = listener; 160 | } 161 | 162 | public interface Listener { 163 | /** 164 | * 悬浮窗显示状态变化 165 | * @param attach 166 | */ 167 | void onFloatWindowAttachChange(boolean attach); 168 | 169 | /** 170 | * 关闭辅助 171 | */ 172 | void onExitService(); 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/dialog/RecordDialog.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.dialog; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.MotionEvent; 6 | import android.view.View; 7 | import android.view.Window; 8 | import android.view.WindowManager; 9 | import android.widget.ImageButton; 10 | 11 | import androidx.annotation.NonNull; 12 | 13 | import com.zhang.autotouch.R; 14 | import com.zhang.autotouch.utils.ToastUtil; 15 | 16 | /** 17 | * 录制模式Dialog 18 | */ 19 | public class RecordDialog extends BaseServiceDialog { 20 | 21 | private ImageButton ibFloat; 22 | 23 | public RecordDialog(@NonNull Context context) { 24 | super(context); 25 | } 26 | 27 | @Override 28 | protected int getLayoutId() { 29 | return R.layout.dialog_record; 30 | } 31 | 32 | @Override 33 | protected int getWidth() { 34 | return WindowManager.LayoutParams.MATCH_PARENT; 35 | } 36 | 37 | @Override 38 | protected int getHeight() { 39 | return WindowManager.LayoutParams.MATCH_PARENT; 40 | } 41 | 42 | @Override 43 | protected void onInited() { 44 | ibFloat = findViewById(R.id.ib_fl); 45 | ibFloat.setOnClickListener(new View.OnClickListener() { 46 | @Override 47 | public void onClick(View v) { 48 | ToastUtil.show("开始录制"); 49 | } 50 | }); 51 | } 52 | 53 | @Override 54 | public void show() { 55 | Window window = getWindow(); 56 | if (window != null) { 57 | WindowManager.LayoutParams params = window.getAttributes(); 58 | //不变暗 59 | params.dimAmount = 0f; 60 | //允许点击穿透 61 | params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 62 | } 63 | super.show(); 64 | } 65 | 66 | @Override 67 | public boolean dispatchTouchEvent(@NonNull MotionEvent ev) { 68 | if (ev.getAction() == MotionEvent.ACTION_UP) { 69 | Log.d("录制坐标", "x=" + ev.getRawX() + "y=" + ev.getRawY()); 70 | } 71 | return super.dispatchTouchEvent(ev); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/fw_permission/Api23CompatImpl.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.fw_permission; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Build; 7 | import android.provider.Settings; 8 | import android.util.Log; 9 | 10 | import java.lang.reflect.Field; 11 | import java.lang.reflect.Method; 12 | 13 | /** 14 | * Android 6.0 以上悬浮窗权限申请实现 15 | */ 16 | public class Api23CompatImpl implements FloatWinPermissionCompat.CompatImpl { 17 | 18 | private static final String TAG = Api23CompatImpl.class.getSimpleName(); 19 | 20 | @Override 21 | public boolean check(Context context) { 22 | boolean result = true; 23 | if (Build.VERSION.SDK_INT >= 23) { 24 | try { 25 | Class clazz = Settings.class; 26 | Method canDrawOverlays = clazz.getDeclaredMethod("canDrawOverlays", Context.class); 27 | result = (Boolean) canDrawOverlays.invoke(null, context); 28 | } catch (Exception e) { 29 | Log.e(TAG, Log.getStackTraceString(e)); 30 | } 31 | } 32 | return result; 33 | } 34 | 35 | @Override 36 | public boolean isSupported() { 37 | return true; 38 | } 39 | 40 | 41 | @Override 42 | public boolean apply(Context context) { 43 | try { 44 | commonROMPermissionApplyInternal(context); 45 | return true; 46 | } catch (Exception e) { 47 | Log.e(TAG, Log.getStackTraceString(e)); 48 | } 49 | return false; 50 | } 51 | 52 | /** 53 | * 通用 rom 权限申请 54 | * 55 | * @param context 56 | * @return 57 | */ 58 | public static void commonROMPermissionApplyInternal(Context context) throws NoSuchFieldException, IllegalAccessException { 59 | Class clazz = Settings.class; 60 | Field field = clazz.getDeclaredField("ACTION_MANAGE_OVERLAY_PERMISSION"); 61 | Intent intent = new Intent(field.get(null).toString()); 62 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 63 | intent.setData(Uri.parse("package:" + context.getPackageName())); 64 | context.startActivity(intent); 65 | //FloatWinPermissionCompat.getInstance().startActivity(intent); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/fw_permission/BelowApi23CompatImpl.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.fw_permission; 2 | 3 | import android.content.Context; 4 | import android.os.Build; 5 | 6 | /** 7 | * Android 6.0 以下的通用实现基类 8 | */ 9 | public abstract class BelowApi23CompatImpl implements FloatWinPermissionCompat.CompatImpl { 10 | 11 | @Override 12 | public boolean check(Context context) { 13 | final int version = Build.VERSION.SDK_INT; 14 | if (version >= 19) { 15 | return FloatWinPermissionCompat.checkOp(context, 24); // 悬浮窗权限的 op 值是 OP_SYSTEM_ALERT_WINDOW = 24; 16 | } 17 | return true; 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/fw_permission/FloatWinPermissionCompat.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.fw_permission; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.app.AppOpsManager; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.os.Binder; 9 | import android.os.Build; 10 | import android.util.Log; 11 | 12 | import java.lang.reflect.Method; 13 | 14 | /** 15 | * 悬浮窗权限兼容类 16 | * 参考了该项目的代码:https://github.com/zhaozepeng/FloatWindowPermission 17 | */ 18 | public class FloatWinPermissionCompat { 19 | 20 | private static final String TAG = FloatWinPermissionCompat.class.getSimpleName(); 21 | 22 | public static FloatWinPermissionCompat getInstance() { 23 | return SingletonHolder.INSTANCE; 24 | } 25 | 26 | private static class SingletonHolder { 27 | private static final FloatWinPermissionCompat INSTANCE = new FloatWinPermissionCompat(); 28 | } 29 | 30 | private CompatImpl compat; 31 | 32 | private FloatWinPermissionCompat() { 33 | // 6.0 以下的处理 34 | if (Build.VERSION.SDK_INT < 23) { 35 | if (RomUtils.isMiui()) { 36 | compat = new MiuiCompatImpl(); 37 | } else if (RomUtils.isMeizu()) { 38 | compat = new MeizuCompatImpl(); 39 | } else if (RomUtils.isHuawei()) { 40 | compat = new HuaweiCompatImpl(); 41 | } else if (RomUtils.isQihoo()) { 42 | compat = new QihooCompatImpl(); 43 | } else { 44 | // Android6.0以下未兼容机型默认实现 45 | compat = new BelowApi23CompatImpl() { 46 | @Override 47 | public boolean isSupported() { 48 | return false; 49 | } 50 | 51 | @Override 52 | public boolean apply(Context context) { 53 | return false; 54 | } 55 | }; 56 | } 57 | } else { 58 | // 魅族单独适配一下 59 | if (RomUtils.isMeizu()) { 60 | compat = new MeizuCompatImpl(); 61 | } else { 62 | // 6.0 版本之后由于 google 增加了对悬浮窗权限的管理,所以方式就统一了 63 | compat = new Api23CompatImpl(); 64 | } 65 | } 66 | } 67 | 68 | /** 69 | * 检查是否已开启悬浮窗权限 70 | * 71 | * @return 72 | */ 73 | public boolean check(Context context) { 74 | return compat.check(context); 75 | } 76 | 77 | /** 78 | * 是否支持打开悬浮窗授权界面 79 | * 80 | * @return 81 | */ 82 | public boolean isSupported() { 83 | return compat.isSupported(); 84 | } 85 | 86 | 87 | /** 88 | * 检测 op 值判断悬浮窗是否已授权 89 | * 90 | * @param context 91 | * @param op 92 | * @return 93 | */ 94 | @TargetApi(Build.VERSION_CODES.KITKAT) 95 | public static boolean checkOp(Context context, int op) { 96 | final int version = Build.VERSION.SDK_INT; 97 | if (version >= 19) { 98 | AppOpsManager manager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); 99 | try { 100 | Class clazz = AppOpsManager.class; 101 | Method method = clazz.getDeclaredMethod("checkOp", int.class, int.class, String.class); 102 | return AppOpsManager.MODE_ALLOWED == (int) method.invoke(manager, op, Binder.getCallingUid(), context.getPackageName()); 103 | } catch (Exception e) { 104 | Log.e(TAG, Log.getStackTraceString(e)); 105 | } 106 | } else { 107 | Log.e(TAG, "Below API 19 cannot invoke!"); 108 | } 109 | return false; 110 | } 111 | 112 | 113 | public interface CompatImpl { 114 | /** 115 | * 检测是否已经权限 116 | * 117 | * @param context 118 | * @return 119 | */ 120 | boolean check(Context context); 121 | 122 | /** 123 | * 对于该 ROM 是否已经做了悬浮窗授权的兼容支持 124 | * 125 | * @return 126 | */ 127 | boolean isSupported(); 128 | 129 | /** 130 | * 申请权限 131 | * 132 | * @param context 133 | * @return 134 | */ 135 | boolean apply(Context context); 136 | } 137 | 138 | /** 139 | * 申请悬浮窗权限 140 | * 141 | * @return 是否成功打开授权界面 142 | */ 143 | public boolean apply(Context context) { 144 | if (!isSupported()) { 145 | return false; 146 | } 147 | forResult = false; 148 | this.context = context; 149 | return compat.apply(context); 150 | } 151 | 152 | public boolean apply(Activity activity) { 153 | if (activity == null || !isSupported()) { 154 | return false; 155 | } 156 | this.activity = activity; 157 | this.context = activity.getApplicationContext(); 158 | forResult = true; 159 | return compat.apply(context); 160 | } 161 | 162 | public static final int REQUEST_CODE_SYSTEM_WINDOW = 1001; 163 | private Activity activity; 164 | private Context context; 165 | private boolean forResult = false; 166 | 167 | public void startActivity(Intent intent) { 168 | try { 169 | if (intent == null || context == null) { 170 | return; 171 | } 172 | if (!forResult) { 173 | context.startActivity(intent); 174 | } else { 175 | if (activity != null) {//为什么打开权限设置页就执行了onActivityResult? 176 | activity.startActivityForResult(intent, REQUEST_CODE_SYSTEM_WINDOW); 177 | } 178 | } 179 | } catch (Exception e) { 180 | e.printStackTrace(); 181 | } 182 | } 183 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/fw_permission/HuaweiCompatImpl.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.fw_permission; 2 | 3 | import android.content.ActivityNotFoundException; 4 | import android.content.ComponentName; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.util.Log; 8 | 9 | /** 10 | * 华为悬浮窗权限兼容实现 11 | */ 12 | public class HuaweiCompatImpl extends BelowApi23CompatImpl { 13 | 14 | private static final String TAG = "HuaweiCompatImpl"; 15 | 16 | @Override 17 | public boolean isSupported() { 18 | return true; 19 | } 20 | 21 | @Override 22 | public boolean apply(Context context) { 23 | try { 24 | Intent intent = new Intent(); 25 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 26 | // ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.permissionmanager.ui.MainActivity");//华为权限管理 27 | // "com.huawei.permissionmanager.ui.SingleAppActivity");//华为权限管理,跳转到指定app的权限管理位置需要华为接口权限,未解决 28 | ComponentName comp = new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");//悬浮窗管理页面 29 | intent.setComponent(comp); 30 | if (RomUtils.getEmuiVersion() == 3.1) { 31 | //emui 3.1 的适配 32 | startActivity(context, intent); 33 | } else { 34 | //emui 3.0 的适配 35 | comp = new ComponentName("com.huawei.systemmanager", "com.huawei.notificationmanager.ui.NotificationManagmentActivity");//悬浮窗管理页面 36 | intent.setComponent(comp); 37 | startActivity(context, intent); 38 | } 39 | } catch (SecurityException e) { 40 | Intent intent = new Intent(); 41 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 42 | ComponentName comp = new ComponentName("com.huawei.systemmanager", 43 | "com.huawei.permissionmanager.ui.MainActivity");//华为权限管理,跳转到本app的权限管理页面,这个需要华为接口权限,未解决 44 | //ComponentName comp = new ComponentName("com.huawei.systemmanager","com.huawei.systemmanager.addviewmonitor.AddViewMonitorActivity");//悬浮窗管理页面 45 | intent.setComponent(comp); 46 | startActivity(context, intent); 47 | Log.e(TAG, Log.getStackTraceString(e)); 48 | } catch (ActivityNotFoundException e) { 49 | /** 50 | * 手机管家版本较低 HUAWEI SC-UL10 51 | */ 52 | Intent intent = new Intent(); 53 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 54 | ComponentName comp = new ComponentName("com.Android.settings", "com.android.settings.permission.TabItem");//权限管理页面 android4.4 55 | //ComponentName comp = new ComponentName("com.android.settings","com.android.settings.permission.single_app_activity");//此处可跳转到指定app对应的权限管理页面,但是需要相关权限,未解决 56 | intent.setComponent(comp); 57 | startActivity(context, intent); 58 | Log.e(TAG, Log.getStackTraceString(e)); 59 | } catch (Exception e) { 60 | //抛出异常时提示信息 61 | Log.e(TAG, Log.getStackTraceString(e)); 62 | } 63 | return false; 64 | } 65 | 66 | private void startActivity(Context context, Intent intent) { 67 | context.startActivity(intent); 68 | //FloatWinPermissionCompat.getInstance().startActivity(intent); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/fw_permission/MeizuCompatImpl.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.fw_permission; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.util.Log; 6 | 7 | /** 8 | * 魅族悬浮窗权限兼容实现 9 | */ 10 | public class MeizuCompatImpl extends BelowApi23CompatImpl { 11 | 12 | @Override 13 | public boolean isSupported() { 14 | return true; 15 | } 16 | 17 | /** 18 | * 去魅族权限申请页面 19 | */ 20 | @Override 21 | public boolean apply(Context context) { 22 | try { 23 | Intent intent = new Intent("com.meizu.safe.security.SHOW_APPSEC"); 24 | intent.setClassName("com.meizu.safe", "com.meizu.safe.security.AppSecActivity"); 25 | intent.putExtra("packageName", context.getPackageName()); 26 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 27 | startActivity(context,intent); 28 | } catch (Exception e) { 29 | try { 30 | Log.d("dq","flyme 6.2.5+,apply permission failed"); 31 | Api23CompatImpl.commonROMPermissionApplyInternal(context); 32 | } catch (Exception eFinal) { 33 | eFinal.printStackTrace(); 34 | } 35 | } 36 | return true; 37 | } 38 | 39 | private void startActivity(Context context, Intent intent) { 40 | context.startActivity(intent); 41 | //FloatWinPermissionCompat.getInstance().startActivity(intent); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/fw_permission/MiuiCompatImpl.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.fw_permission; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.provider.Settings; 7 | import android.util.Log; 8 | 9 | /** 10 | * MIUI 悬浮窗权限兼容实现 11 | */ 12 | public class MiuiCompatImpl extends BelowApi23CompatImpl { 13 | 14 | private static final String TAG = "MiuiCompatImpl"; 15 | private int miuiVersion = -1; 16 | 17 | public MiuiCompatImpl() { 18 | miuiVersion = RomUtils.getMiuiVersion(); 19 | } 20 | 21 | @Override 22 | public boolean isSupported() { 23 | return miuiVersion >= 5 && miuiVersion <= 8; 24 | } 25 | 26 | @Override 27 | public boolean apply(Context context) { 28 | if (miuiVersion == 5) { 29 | return applyV5(context); 30 | } else if (miuiVersion == 6) { 31 | return applyV6(context); 32 | } else if (miuiVersion == 7) { 33 | return applyV7(context); 34 | } else if (miuiVersion == 8) { 35 | return applyV8(context); 36 | } else { 37 | Log.e(TAG, "this is a special MIUI rom version, its version code " + miuiVersion); 38 | } 39 | return false; 40 | } 41 | 42 | /** 43 | * MIUI8下申请权限 44 | * 45 | * @param context 46 | */ 47 | private boolean applyV8(Context context) { 48 | Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); 49 | intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity"); 50 | intent.putExtra("extra_pkgname", context.getPackageName()); 51 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 52 | if (RomUtils.isIntentAvailable(context, intent)) { 53 | startActivity(context, intent); 54 | return true; 55 | } 56 | 57 | intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); 58 | intent.setPackage("com.miui.securitycenter"); 59 | intent.putExtra("extra_pkgname", context.getPackageName()); 60 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 61 | if (RomUtils.isIntentAvailable(context, intent)) { 62 | startActivity(context, intent); 63 | return true; 64 | } else { 65 | // 小米平板2上没有安全中心,可以打开应用详情页开启权限 66 | //I/ActivityManager: START u0 {flg=0x14000000 cmp=com.android.settings/.applications.InstalledAppDetailsTop (has extras)} 67 | return applyV5(context); 68 | } 69 | } 70 | 71 | /** 72 | * MIUI7下申请权限 73 | * 74 | * @param context 75 | */ 76 | private boolean applyV7(Context context) { 77 | Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); 78 | intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); 79 | intent.putExtra("extra_pkgname", context.getPackageName()); 80 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 81 | if (RomUtils.isIntentAvailable(context, intent)) { 82 | startActivity(context, intent); 83 | return true; 84 | } else { 85 | Log.e(TAG, "applyV7 Intent is not available!"); 86 | } 87 | return false; 88 | } 89 | 90 | /** 91 | * MIUI6下申请权限 92 | * 93 | * @param context 94 | */ 95 | private boolean applyV6(Context context) { 96 | Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR"); 97 | intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.AppPermissionsEditorActivity"); 98 | intent.putExtra("extra_pkgname", context.getPackageName()); 99 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 100 | if (RomUtils.isIntentAvailable(context, intent)) { 101 | startActivity(context, intent); 102 | return true; 103 | } else { 104 | Log.e(TAG, "applyV6 Intent is not available!"); 105 | } 106 | return false; 107 | } 108 | 109 | /** 110 | * MIUI5下申请权限 111 | * 112 | * @param context 113 | */ 114 | private boolean applyV5(Context context) { 115 | Intent intent; 116 | String packageName = context.getPackageName(); 117 | intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); 118 | Uri uri = Uri.fromParts("package", packageName, null); 119 | intent.setData(uri); 120 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 121 | if (RomUtils.isIntentAvailable(context, intent)) { 122 | startActivity(context, intent); 123 | return true; 124 | } else { 125 | Log.e(TAG, "applyV5 intent is not available!"); 126 | } 127 | return false; 128 | //设置页面在应用详情页面 129 | } 130 | 131 | private void startActivity(Context context, Intent intent) { 132 | context.startActivity(intent); 133 | //FloatWinPermissionCompat.getInstance().startActivity(intent); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/fw_permission/QihooCompatImpl.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.fw_permission; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.util.Log; 6 | 7 | /** 8 | * 360 悬浮窗权限兼容实现 9 | */ 10 | public class QihooCompatImpl extends BelowApi23CompatImpl { 11 | 12 | private static final String TAG = "QihooCompatImpl"; 13 | 14 | @Override 15 | public boolean isSupported() { 16 | return true; 17 | } 18 | 19 | @Override 20 | public boolean apply(Context context) { 21 | Intent intent = new Intent(); 22 | intent.setClassName("com.android.settings", "com.android.settings.Settings$OverlaySettingsActivity"); 23 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 24 | if (RomUtils.isIntentAvailable(context, intent)) { 25 | startActivity(context,intent); 26 | return true; 27 | } else { 28 | intent.setClassName("com.qihoo360.mobilesafe", "com.qihoo360.mobilesafe.ui.index.appEnterActivity"); 29 | if (RomUtils.isIntentAvailable(context, intent)) { 30 | startActivity(context,intent); 31 | return true; 32 | } else { 33 | Log.e(TAG, "can't open permission page with particular name, please use " + 34 | "\"adb shell dumpsys activity\" command and tell me the name of the float window permission page"); 35 | } 36 | } 37 | return false; 38 | } 39 | 40 | private void startActivity(Context context, Intent intent) { 41 | context.startActivity(intent); 42 | //FloatWinPermissionCompat.getInstance().startActivity(intent); 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/fw_permission/RomUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2016 Facishare Technology Co., Ltd. All Rights Reserved. 3 | */ 4 | package com.zhang.autotouch.fw_permission; 5 | 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.pm.PackageManager; 9 | import android.os.Build; 10 | import android.text.TextUtils; 11 | import android.util.Log; 12 | 13 | import java.io.BufferedReader; 14 | import java.io.IOException; 15 | import java.io.InputStreamReader; 16 | 17 | /** 18 | * Rom 工具类 19 | */ 20 | public class RomUtils { 21 | 22 | private static final String TAG = RomUtils.class.getSimpleName(); 23 | 24 | /** 25 | * 获取 emui 版本号 26 | * 27 | * @return 28 | */ 29 | public static double getEmuiVersion() { 30 | try { 31 | String emuiVersion = getSystemProperty("ro.build.version.emui"); 32 | String version = emuiVersion.substring(emuiVersion.indexOf("_") + 1); 33 | return Double.parseDouble(version); 34 | } catch (Exception e) { 35 | e.printStackTrace(); 36 | } 37 | return 4.0; 38 | } 39 | 40 | /** 41 | * 获取小米 rom 版本号,获取失败返回 -1 42 | * 43 | * @return miui rom version code, if fail , return -1 44 | */ 45 | public static int getMiuiVersion() { 46 | String version = getSystemProperty("ro.miui.ui.version.name"); 47 | if (version != null) { 48 | try { 49 | return Integer.parseInt(version.substring(1)); 50 | } catch (Exception e) { 51 | Log.e(TAG, "getInstance miui version code error, version : " + version); 52 | } 53 | } 54 | return -1; 55 | } 56 | 57 | /** 58 | * System.getProperties()不返回与 getprop 相同的属性。要获取 getprop 属性,请尝试使用 Runtime.exec() 执行 getprop 并读取其标准输出。 59 | * 60 | * @param propName 61 | * @return 62 | */ 63 | public static String getSystemProperty(String propName) { 64 | String line; 65 | BufferedReader input = null; 66 | try { 67 | Process p = Runtime.getRuntime().exec("getprop " + propName); 68 | input = new BufferedReader(new InputStreamReader(p.getInputStream()), 1024); 69 | line = input.readLine(); 70 | input.close(); 71 | } catch (IOException ex) { 72 | Log.e(TAG, "Unable to read sysprop " + propName, ex); 73 | return null; 74 | } finally { 75 | if (input != null) { 76 | try { 77 | input.close(); 78 | } catch (IOException e) { 79 | Log.e(TAG, "Exception while closing InputStream", e); 80 | } 81 | } 82 | } 83 | return line; 84 | } 85 | 86 | /** 87 | * 是否OPPO系统 88 | * 89 | * @return 90 | */ 91 | public static boolean isOppo() { 92 | return !TextUtils.isEmpty(getSystemProperty("ro.build.version.opporom")); 93 | } 94 | 95 | /** 96 | * 是否VIVO系统 97 | * 98 | * @return 99 | */ 100 | public static boolean isVivo() { 101 | return !TextUtils.isEmpty(getSystemProperty("ro.vivo.os.version")); 102 | } 103 | 104 | /** 105 | * 是否华为系统 106 | * 107 | * @return 108 | */ 109 | public static boolean isHuawei() { 110 | return Build.MANUFACTURER.contains("HUAWEI"); 111 | } 112 | 113 | /** 114 | * 是否小米系统 115 | * 116 | * @return 117 | */ 118 | public static boolean isMiui() { 119 | return !TextUtils.isEmpty(getSystemProperty("ro.miui.ui.version.name")); 120 | } 121 | 122 | /** 123 | * 是否魅族系统 124 | * 125 | * @return 126 | */ 127 | public static boolean isMeizu() { 128 | String meizuFlymeOSFlag = getSystemProperty("ro.build.display.id"); 129 | return !TextUtils.isEmpty(meizuFlymeOSFlag) && meizuFlymeOSFlag.toLowerCase().contains("flyme"); 130 | } 131 | 132 | /** 133 | * 是否 360 系统 134 | * 135 | * @return 136 | */ 137 | public static boolean isQihoo() { 138 | return Build.MANUFACTURER.contains("QiKU"); 139 | } 140 | 141 | 142 | /** 143 | * 判断 Intent 是否有效 144 | * 145 | * @param context 146 | * @param intent 147 | * @return 148 | */ 149 | public static boolean isIntentAvailable(Context context, Intent intent) { 150 | if (intent == null) { 151 | return false; 152 | } 153 | return context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY).size() > 0; 154 | } 155 | 156 | 157 | } 158 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/listener/ScreenListener.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.listener; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.IntentFilter; 7 | import android.os.PowerManager; 8 | 9 | /** 10 | * Created by ${zyj} on 2016/6/21. 11 | */ 12 | public class ScreenListener { 13 | 14 | private Context mContext; 15 | private ScreenBroadcastReceiver mScreenReceiver; 16 | private ScreenStateListener mScreenStateListener; 17 | 18 | public ScreenListener(Context context) { 19 | mContext = context; 20 | mScreenReceiver = new ScreenBroadcastReceiver(); 21 | } 22 | 23 | /** 24 | * screen状态广播接收者 25 | */ 26 | private class ScreenBroadcastReceiver extends BroadcastReceiver { 27 | private String action = null; 28 | 29 | @Override 30 | public void onReceive(Context context, Intent intent) { 31 | action = intent.getAction(); 32 | if (Intent.ACTION_SCREEN_ON.equals(action)) { // 开屏 33 | mScreenStateListener.onScreenOn(); 34 | } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 锁屏 35 | mScreenStateListener.onScreenOff(); 36 | } else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解锁 37 | mScreenStateListener.onUserPresent(); 38 | } 39 | } 40 | } 41 | /** 42 | * 开始监听screen状态 43 | * 44 | * @param listener 45 | */ 46 | public void begin(ScreenStateListener listener) { 47 | mScreenStateListener = listener; 48 | registerListener(); 49 | getScreenState(); 50 | } 51 | 52 | /** 53 | * 获取screen状态 54 | */ 55 | private void getScreenState() { 56 | PowerManager manager = (PowerManager) mContext 57 | .getSystemService(Context.POWER_SERVICE); 58 | if (manager.isScreenOn()) { 59 | if (mScreenStateListener != null) { 60 | mScreenStateListener.onScreenOn(); 61 | } 62 | } else { 63 | if (mScreenStateListener != null) { 64 | mScreenStateListener.onScreenOff(); 65 | } 66 | } 67 | } 68 | 69 | /** 70 | * 停止screen状态监听 71 | */ 72 | public void unregisterListener() { 73 | mContext.unregisterReceiver(mScreenReceiver); 74 | } 75 | 76 | /** 77 | * 启动screen状态广播接收器 78 | */ 79 | private void registerListener() { 80 | IntentFilter filter = new IntentFilter(); 81 | filter.addAction(Intent.ACTION_SCREEN_ON); 82 | filter.addAction(Intent.ACTION_SCREEN_OFF); 83 | filter.addAction(Intent.ACTION_USER_PRESENT); 84 | mContext.registerReceiver(mScreenReceiver, filter); 85 | } 86 | 87 | public interface ScreenStateListener {// 返回给调用者屏幕状态信息 88 | public void onScreenOn(); 89 | 90 | public void onScreenOff(); 91 | 92 | public void onUserPresent(); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/service/AutoTouchService.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.service; 2 | 3 | import android.accessibilityservice.AccessibilityService; 4 | import android.accessibilityservice.GestureDescription; 5 | import android.annotation.SuppressLint; 6 | import android.graphics.Path; 7 | import android.graphics.Rect; 8 | import android.os.Build; 9 | import android.os.Handler; 10 | import android.os.Looper; 11 | import android.text.TextUtils; 12 | import android.util.Log; 13 | import android.view.Gravity; 14 | import android.view.KeyEvent; 15 | import android.view.LayoutInflater; 16 | import android.view.WindowManager; 17 | import android.view.accessibility.AccessibilityEvent; 18 | import android.view.accessibility.AccessibilityNodeInfo; 19 | import android.widget.TextView; 20 | 21 | import androidx.annotation.RequiresApi; 22 | 23 | import com.zhang.autotouch.R; 24 | import com.zhang.autotouch.TouchEventManager; 25 | import com.zhang.autotouch.bean.TouchEvent; 26 | import com.zhang.autotouch.bean.TouchPoint; 27 | import com.zhang.autotouch.utils.AccessibilityNodeInfoUtil; 28 | import com.zhang.autotouch.utils.CopyPasteUtil; 29 | import com.zhang.autotouch.utils.DensityUtil; 30 | import com.zhang.autotouch.utils.KeyboardDownUtil; 31 | import com.zhang.autotouch.utils.LoopArrayList; 32 | import com.zhang.autotouch.utils.SpUtils; 33 | import com.zhang.autotouch.utils.WindowUtils; 34 | 35 | import org.greenrobot.eventbus.EventBus; 36 | import org.greenrobot.eventbus.Subscribe; 37 | import org.greenrobot.eventbus.ThreadMode; 38 | 39 | import java.text.DecimalFormat; 40 | import java.util.Collections; 41 | import java.util.List; 42 | import java.util.Random; 43 | 44 | /** 45 | * 无障碍服务-自动点击 46 | * 47 | * @date 2019/9/6 16:23 48 | */ 49 | @RequiresApi(api = Build.VERSION_CODES.N) 50 | public class AutoTouchService extends AccessibilityService { 51 | 52 | private final String TAG = "AutoTouchService+++"; 53 | private final List autoTouchPointList = new LoopArrayList<>(); 54 | private int index = 0; 55 | private final DecimalFormat floatDf = new DecimalFormat("#0.0"); 56 | //自动点击事件 57 | private TouchPoint autoTouchPoint; 58 | @SuppressLint("HandlerLeak") 59 | private Handler handler = new Handler(Looper.getMainLooper()); 60 | private WindowManager windowManager; 61 | private TextView tvTouchPoint; 62 | //倒计时 63 | private float countDownTime; 64 | //修改点击文本的倒计时 65 | private Runnable touchViewRunnable; 66 | private final Runnable autoTouchRunnable = new Runnable() { 67 | @Override 68 | public void run() { 69 | 70 | // if (!TextUtils.isEmpty(autoTouchPoint.getText())) { 71 | // AccessibilityNodeInfo nodeInfo 72 | // = AccessibilityNodeInfoUtil.getFocusAccessibilityNodeInfoButton(AutoTouchService.this, "发送"); 73 | // if (nodeInfo != null) { 74 | // CopyPasteUtil.pasteTextOnFocus( 75 | // AutoTouchService.this 76 | // , null 77 | // , autoTouchPoint.getText() == null ? "赞👍" : autoTouchPoint.getText() 78 | // ); 79 | // Rect rect = new Rect(); 80 | // nodeInfo.getBoundsInScreen(rect); 81 | // Log.d(TAG, "forceClick: " + rect.left + " " + rect.top + " " + rect.right + " " + rect.bottom); 82 | // int x = (rect.left + rect.right) / 2; 83 | // int y = (rect.top + rect.bottom) / 2; 84 | // keyDownMethod(new TouchPoint("发送", x, y, autoTouchPoint.getDelay())); 85 | // } else { 86 | // KeyboardDownUtil.keyDown(KeyEvent.KEYCODE_ENTER); 87 | // } 88 | // } 89 | keyDownMethod(autoTouchPoint); 90 | onAutoClick(); 91 | } 92 | }; 93 | 94 | @Override 95 | protected void onServiceConnected() { 96 | super.onServiceConnected(); 97 | handler = new Handler(); 98 | EventBus.getDefault().register(this); 99 | windowManager = WindowUtils.getWindowManager(this); 100 | } 101 | 102 | @Subscribe(threadMode = ThreadMode.MAIN) 103 | public void onReciverTouchEvent(TouchEvent event) { 104 | Log.d(TAG, "onReciverTouchEvent: " + event.toString()); 105 | TouchEventManager.getInstance().setTouchAction(event.getAction()); 106 | handler.removeCallbacks(autoTouchRunnable); 107 | switch (event.getAction()) { 108 | case TouchEvent.ACTION_START: 109 | autoTouchPointList.clear(); 110 | if (event.getTouchPoint() == null) { 111 | autoTouchPointList.addAll(SpUtils.getTouchPoints(AutoTouchService.this)); 112 | if (autoTouchPointList.size() == 0) { 113 | return; 114 | } 115 | } else { 116 | autoTouchPointList.add(event.getTouchPoint()); 117 | } 118 | index = 0; 119 | onAutoClick(); 120 | break; 121 | case TouchEvent.ACTION_CONTINUE: 122 | if (autoTouchPoint != null) { 123 | onAutoClick(); 124 | } 125 | break; 126 | case TouchEvent.ACTION_PAUSE: 127 | handler.removeCallbacks(autoTouchRunnable); 128 | handler.removeCallbacks(touchViewRunnable); 129 | break; 130 | case TouchEvent.ACTION_STOP: 131 | handler.removeCallbacks(autoTouchRunnable); 132 | handler.removeCallbacks(touchViewRunnable); 133 | removeTouchView(); 134 | autoTouchPoint = null; 135 | break; 136 | } 137 | } 138 | 139 | /** 140 | * 执行自动点击 141 | */ 142 | private void onAutoClick() { 143 | autoTouchPoint = autoTouchPointList.get(index++); 144 | if (autoTouchPoint != null) { 145 | handler.postDelayed(autoTouchRunnable, getDelayTime()); 146 | showTouchView(); 147 | Log.e("自动点击", "自动点击"); 148 | } 149 | } 150 | 151 | private void keyDownMethod(TouchPoint autoTouchPoint) { 152 | Log.d(TAG, "onAutoClick: " + "x=" + autoTouchPoint.getX() + " y=" + autoTouchPoint.getY()); 153 | Path path = new Path(); 154 | float rand = new Random().nextFloat(); 155 | path.moveTo(autoTouchPoint.getX() + rand, autoTouchPoint.getY() + rand); 156 | GestureDescription.Builder builder = new GestureDescription.Builder(); 157 | GestureDescription gestureDescription = builder.addStroke( 158 | new GestureDescription.StrokeDescription(path, 0, 100)) 159 | .build(); 160 | dispatchGesture(gestureDescription, new AccessibilityService.GestureResultCallback() { 161 | @Override 162 | public void onCompleted(GestureDescription gestureDescription) { 163 | super.onCompleted(gestureDescription); 164 | Log.d("AutoTouchService", "滑动结束" + gestureDescription.getStrokeCount()); 165 | } 166 | 167 | @Override 168 | public void onCancelled(GestureDescription gestureDescription) { 169 | super.onCancelled(gestureDescription); 170 | Log.d("AutoTouchService", "滑动取消"); 171 | } 172 | }, null); 173 | } 174 | 175 | private long getDelayTime() { 176 | // int random = (int) (Math.random() * (30 - 1) + 1); 177 | // return autoTouchEvent.getDelay() * 1000L + random; 178 | float f = new Random().nextFloat(); 179 | long current = (long) (500 + f * 500L); 180 | return autoTouchPoint.getDelay() * current; 181 | } 182 | 183 | @Override 184 | public void onAccessibilityEvent(AccessibilityEvent event) { 185 | } 186 | 187 | @Override 188 | public void onInterrupt() { 189 | } 190 | 191 | @Override 192 | public void onDestroy() { 193 | super.onDestroy(); 194 | handler.removeCallbacksAndMessages(null); 195 | removeTouchView(); 196 | } 197 | 198 | /** 199 | * 显示倒计时 200 | */ 201 | private void showTouchView() { 202 | if (autoTouchPoint != null) { 203 | //创建触摸点View 204 | if (tvTouchPoint == null) { 205 | tvTouchPoint = (TextView) LayoutInflater.from(this).inflate(R.layout.window_touch_point, null); 206 | } 207 | //显示触摸点View 208 | if (windowManager != null && !tvTouchPoint.isAttachedToWindow()) { 209 | int width = DensityUtil.dip2px(this, 40); 210 | int height = DensityUtil.dip2px(this, 40); 211 | WindowManager.LayoutParams params = WindowUtils.newWmParams(width, height); 212 | params.gravity = Gravity.START | Gravity.TOP; 213 | params.x = autoTouchPoint.getX() - width / 2; 214 | params.y = autoTouchPoint.getY() - width; 215 | params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 216 | windowManager.addView(tvTouchPoint, params); 217 | } 218 | //开启倒计时 219 | countDownTime = autoTouchPoint.getDelay(); 220 | if (touchViewRunnable == null) { 221 | touchViewRunnable = new Runnable() { 222 | @Override 223 | public void run() { 224 | handler.removeCallbacks(touchViewRunnable); 225 | Log.d("触摸倒计时", countDownTime + ""); 226 | if (countDownTime > 0) { 227 | float offset = 0.1f; 228 | tvTouchPoint.setText(floatDf.format(countDownTime)); 229 | countDownTime -= offset; 230 | handler.postDelayed(touchViewRunnable, (long) (500L * offset)); 231 | } else { 232 | removeTouchView(); 233 | } 234 | } 235 | }; 236 | } 237 | handler.post(touchViewRunnable); 238 | } 239 | } 240 | 241 | private void removeTouchView() { 242 | if (windowManager != null && tvTouchPoint != null && tvTouchPoint.isAttachedToWindow()) { 243 | windowManager.removeView(tvTouchPoint); 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/service/FloatingService.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.service; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Dialog; 5 | import android.app.Service; 6 | import android.content.Intent; 7 | import android.os.Handler; 8 | import android.os.IBinder; 9 | import android.view.Gravity; 10 | import android.view.LayoutInflater; 11 | import android.view.MotionEvent; 12 | import android.view.View; 13 | import android.view.WindowManager; 14 | 15 | import com.zhang.autotouch.R; 16 | import com.zhang.autotouch.bean.TouchEvent; 17 | import com.zhang.autotouch.dialog.MenuDialog; 18 | import com.zhang.autotouch.listener.ScreenListener; 19 | import com.zhang.autotouch.utils.DensityUtil; 20 | import com.zhang.autotouch.utils.ToastUtil; 21 | import com.zhang.autotouch.utils.WindowUtils; 22 | 23 | /**A 24 | * 悬浮窗 25 | */ 26 | public class FloatingService extends Service { 27 | private WindowManager mWindowManager; 28 | private View mFloatingView; 29 | private MenuDialog menuDialog; 30 | private WindowManager.LayoutParams floatLayoutParams; 31 | private ScreenListener screenListener; 32 | 33 | @Override 34 | public IBinder onBind(Intent intent) { 35 | return null; 36 | } 37 | 38 | @Override 39 | public void onCreate() { 40 | super.onCreate(); 41 | mFloatingView = creatView(R.layout.layout_window); 42 | //设置WindowManger布局参数以及相关属性 43 | int d = DensityUtil.dip2px(this, 50); 44 | floatLayoutParams = WindowUtils.newWmParams(d, d); 45 | //初始化位置 46 | floatLayoutParams.gravity = Gravity.TOP | Gravity.START; 47 | floatLayoutParams.x = WindowUtils.getScreenWidth(this) - DensityUtil.dip2px(this, 80); 48 | floatLayoutParams.y = WindowUtils.getScreenHeight(this) - DensityUtil.dip2px(this, 200); 49 | //获取WindowManager对象 50 | mWindowManager = WindowUtils.getWindowManager(this); 51 | addViewToWindow(mFloatingView, floatLayoutParams); 52 | //FloatingView的拖动事件 53 | mFloatingView.setClickable(true); 54 | mFloatingView.setOnTouchListener(new View.OnTouchListener() { 55 | private int x; 56 | private int y; 57 | //是否在移动 58 | private boolean isMoving; 59 | 60 | @SuppressLint("ClickableViewAccessibility") 61 | @Override 62 | public boolean onTouch(View v, MotionEvent event) { 63 | switch (event.getAction()) { 64 | case MotionEvent.ACTION_DOWN: 65 | x = (int) event.getRawX(); 66 | y = (int) event.getRawY(); 67 | isMoving = false; 68 | return true; 69 | case MotionEvent.ACTION_MOVE: 70 | int nowX = (int) event.getRawX(); 71 | int nowY = (int) event.getRawY(); 72 | int moveX = nowX - x; 73 | int moveY = nowY - y; 74 | if (Math.abs(moveX) > 0 || Math.abs(moveY) > 0) { 75 | isMoving = true; 76 | floatLayoutParams.x += moveX; 77 | floatLayoutParams.y += moveY; 78 | //更新View的位置 79 | mWindowManager.updateViewLayout(mFloatingView, floatLayoutParams); 80 | x = nowX; 81 | y = nowY; 82 | return true; 83 | } 84 | break; 85 | case MotionEvent.ACTION_UP: 86 | if (!isMoving) { 87 | onShowSelectDialog(); 88 | return true; 89 | } 90 | break; 91 | } 92 | return false; 93 | } 94 | }); 95 | initView(); 96 | initLiatener(); 97 | 98 | } 99 | 100 | private void initView() { 101 | new Handler().postDelayed(new Runnable() { 102 | @Override 103 | public void run() { 104 | onShowSelectDialog(); 105 | } 106 | }, 800); 107 | } 108 | 109 | private void initLiatener() { 110 | screenListener = new ScreenListener(this); 111 | screenListener.begin(new ScreenListener.ScreenStateListener() { 112 | @Override 113 | public void onScreenOn() { 114 | //Toast.makeText( MainActivity.this , "屏幕打开了" , Toast.LENGTH_SHORT ).show(); 115 | } 116 | 117 | @Override 118 | public void onScreenOff() { 119 | //Toast.makeText( MainActivity.this , "屏幕关闭了" , Toast.LENGTH_SHORT ).show(); 120 | TouchEvent.postStopAction(); 121 | ToastUtil.show("已停止触控"); 122 | } 123 | 124 | @Override 125 | public void onUserPresent() { 126 | //Toast.makeText( MainActivity.this , "解锁了" , Toast.LENGTH_SHORT ).show(); 127 | } 128 | }); 129 | 130 | } 131 | 132 | 133 | @SuppressLint("ClickableViewAccessibility") 134 | private void onShowSelectDialog() { 135 | //弹出菜单弹窗 136 | hideDialog(menuDialog); 137 | if (menuDialog == null) { 138 | menuDialog = new MenuDialog(this); 139 | menuDialog.setListener(new MenuDialog.Listener() { 140 | @Override 141 | public void onFloatWindowAttachChange(boolean attach) { 142 | if (attach) { 143 | addViewToWindow(mFloatingView, floatLayoutParams); 144 | } else { 145 | removeViewFromWinddow(mFloatingView); 146 | } 147 | } 148 | 149 | @Override 150 | public void onExitService() { 151 | stopSelf(); 152 | } 153 | }); 154 | } 155 | menuDialog.show(); 156 | } 157 | 158 | @Override 159 | public void onDestroy() { 160 | super.onDestroy(); 161 | removeViewFromWinddow(mFloatingView); 162 | hideDialog(menuDialog); 163 | if (screenListener != null) screenListener.unregisterListener(); 164 | } 165 | 166 | private void hideDialog(Dialog dialog) { 167 | if (dialog != null && dialog.isShowing()) { 168 | dialog.dismiss(); 169 | } 170 | } 171 | 172 | private void addViewToWindow(View view, WindowManager.LayoutParams params) { 173 | if (mWindowManager != null) { 174 | mWindowManager.addView(view, params); 175 | } 176 | } 177 | 178 | private void removeViewFromWinddow(View view) { 179 | if (mWindowManager != null && view != null && view.isAttachedToWindow()) { 180 | mWindowManager.removeView(view); 181 | } 182 | } 183 | 184 | private T creatView(int layout) { 185 | return (T) LayoutInflater.from(this).inflate(layout, null); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/utils/AccessibilityNodeInfoUtil.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.utils; 2 | 3 | import android.accessibilityservice.AccessibilityService; 4 | import android.view.accessibility.AccessibilityNodeInfo; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class AccessibilityNodeInfoUtil { 10 | 11 | public static AccessibilityNodeInfo getFocusAccessibilityNodeInfo(AccessibilityService accessibilityService, String focusText) { 12 | AccessibilityNodeInfo root = accessibilityService.getRootInActiveWindow(); 13 | if (root == null) return null; 14 | 15 | // 1 16 | AccessibilityNodeInfo info = root.findFocus(AccessibilityNodeInfo.FOCUS_ACCESSIBILITY); 17 | // 2 18 | if (info == null) { 19 | info = root.findFocus(AccessibilityNodeInfo.FOCUS_INPUT); 20 | } 21 | // 3 22 | if (info == null && focusText != null) { 23 | List nodeInfoList = root.findAccessibilityNodeInfosByText(focusText); 24 | if (nodeInfoList.size() > 0) { 25 | AccessibilityNodeInfo tmp = nodeInfoList.get(0); 26 | if (tmp != null) { 27 | CharSequence charSequence = tmp.getText(); 28 | if (charSequence != null && charSequence.equals(focusText)) { 29 | info = tmp; 30 | } 31 | } 32 | } 33 | } 34 | // 4 35 | if (info == null && focusText != null) { 36 | List childList = new ArrayList<>(); 37 | recycle(root, childList); 38 | if (childList.size() > 0) { 39 | for (AccessibilityNodeInfo tmp : childList) { 40 | if (tmp != null) { 41 | CharSequence charSequence = tmp.getText(); 42 | if (charSequence != null && charSequence.equals(focusText)) { 43 | info = tmp; 44 | } 45 | } 46 | } 47 | } 48 | } 49 | return info; 50 | } 51 | 52 | public static AccessibilityNodeInfo getFocusAccessibilityNodeInfoButton(AccessibilityService accessibilityService, String focusText) { 53 | AccessibilityNodeInfo root = accessibilityService.getRootInActiveWindow(); 54 | if (root == null) return null; 55 | 56 | // 1 57 | AccessibilityNodeInfo info = null; 58 | // 3 59 | if (info == null && focusText != null) { 60 | List nodeInfoList = root.findAccessibilityNodeInfosByText(focusText); 61 | if (nodeInfoList.size() > 0) { 62 | AccessibilityNodeInfo tmp = nodeInfoList.get(0); 63 | if (tmp != null) { 64 | CharSequence charSequence = tmp.getText(); 65 | if (charSequence != null && charSequence.equals(focusText)) { 66 | info = tmp; 67 | } 68 | } 69 | } 70 | } 71 | // 4 72 | if (info == null && focusText != null) { 73 | List childList = new ArrayList<>(); 74 | recycle(root, childList); 75 | if (childList.size() > 0) { 76 | for (AccessibilityNodeInfo tmp : childList) { 77 | if (tmp != null) { 78 | CharSequence charSequence = tmp.getText(); 79 | if (charSequence != null && charSequence.equals(focusText)) { 80 | info = tmp; 81 | } 82 | } 83 | } 84 | } 85 | } 86 | return info; 87 | } 88 | 89 | private static void recycle(AccessibilityNodeInfo root, List childList) { 90 | if (root.getChildCount() == 0) { 91 | childList.add(root); 92 | } else { 93 | for (int i = 0; i < root.getChildCount(); i++) { 94 | if (root.getChild(i) != null) { 95 | recycle(root.getChild(i), childList); 96 | } 97 | } 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/utils/AccessibilityUtil.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.utils; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.provider.Settings; 6 | import android.text.TextUtils; 7 | import android.util.Log; 8 | 9 | import static android.content.ContentValues.TAG; 10 | 11 | /** 12 | * @author 张坚鸿 13 | * @date 2019/9/6 16:32 14 | */ 15 | public class AccessibilityUtil { 16 | 17 | public static boolean isSettingOpen(Class service, Context cxt) { 18 | try { 19 | int enable = Settings.Secure.getInt(cxt.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0); 20 | if (enable != 1) 21 | return false; 22 | String services = Settings.Secure.getString(cxt.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); 23 | if (!TextUtils.isEmpty(services)) { 24 | TextUtils.SimpleStringSplitter split = new TextUtils.SimpleStringSplitter(':'); 25 | split.setString(services); 26 | // 遍历所有已开启的辅助服务名 27 | while (split.hasNext()) { 28 | if (split.next().equalsIgnoreCase(cxt.getPackageName() + "/" + service.getName())) { 29 | return true; 30 | } 31 | } 32 | } 33 | } catch (Throwable e) {//若出现异常,则说明该手机设置被厂商篡改了,需要适配 34 | Log.e(TAG, "isSettingOpen: " + e.getMessage()); 35 | } 36 | return false; 37 | } 38 | 39 | /** 40 | * 跳转到系统设置:开启辅助服务 41 | */ 42 | public static void jumpToSetting(final Context cxt) { 43 | try { 44 | cxt.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)); 45 | } catch (Throwable e) {//若出现异常,则说明该手机设置被厂商篡改了,需要适配 46 | try { 47 | Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS); 48 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 49 | cxt.startActivity(intent); 50 | } catch (Throwable e2) { 51 | Log.e(TAG, "jumpToSetting: " + e2.getMessage()); 52 | } 53 | } 54 | } 55 | } 56 | 57 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/utils/CopyPasteUtil.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.utils; 2 | 3 | import android.accessibilityservice.AccessibilityService; 4 | import android.annotation.SuppressLint; 5 | import android.content.ClipData; 6 | import android.content.ClipboardManager; 7 | import android.content.Context; 8 | import android.os.Build; 9 | import android.os.Bundle; 10 | import android.view.accessibility.AccessibilityNodeInfo; 11 | 12 | import static android.content.Context.CLIPBOARD_SERVICE; 13 | 14 | public class CopyPasteUtil { 15 | 16 | /** 17 | * 输入文本 18 | */ 19 | public static void pasteTextOnFocus(AccessibilityService accessibilityService, String focusText, String pasteText) { 20 | AccessibilityNodeInfo info = AccessibilityNodeInfoUtil.getFocusAccessibilityNodeInfo(accessibilityService, focusText); 21 | // 复制文字 22 | if (info != null) { 23 | pasteText(accessibilityService, info, pasteText); 24 | } 25 | } 26 | 27 | @SuppressLint("ObsoleteSdkInt") 28 | private static void pasteText(Context context, AccessibilityNodeInfo info, String text) { 29 | //粘贴板 30 | ClipboardManager clipboard = (ClipboardManager) context.getSystemService(CLIPBOARD_SERVICE); 31 | ClipData clip = ClipData.newPlainText("label", text); 32 | clipboard.setPrimaryClip(clip); 33 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 34 | Bundle arguments = new Bundle(); 35 | arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, text); 36 | info.performAction(AccessibilityNodeInfo.ACTION_FOCUS); 37 | info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments); 38 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 39 | Bundle arguments = new Bundle(); 40 | arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 0); 41 | arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, text.length()); 42 | info.performAction(AccessibilityNodeInfo.ACTION_FOCUS); 43 | info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments); 44 | info.performAction(AccessibilityNodeInfo.ACTION_PASTE); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/utils/DensityUtil.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.utils; 2 | 3 | import android.content.Context; 4 | 5 | public class DensityUtil { 6 | public static int dip2px(Context context, float dpValue) { 7 | final float scale = context.getResources().getDisplayMetrics().density; 8 | return (int) (dpValue * scale + 0.5f); 9 | } 10 | 11 | public static int px2dip(Context context, float pxValue) { 12 | final float scale = context.getResources().getDisplayMetrics().density; 13 | return (int) (pxValue / scale + 0.5f); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/utils/DialogUtils.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.utils; 2 | 3 | import android.app.Dialog; 4 | 5 | public class DialogUtils { 6 | 7 | public static void dismiss(Dialog dialog) { 8 | if (dialog != null && dialog.isShowing()) { 9 | dialog.dismiss(); 10 | } 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/utils/GsonUtils.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.utils; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.JsonArray; 5 | import com.google.gson.JsonElement; 6 | import com.google.gson.JsonParser; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class GsonUtils { 12 | 13 | private static Gson gson; 14 | 15 | static { 16 | gson = new Gson(); 17 | } 18 | 19 | public static T jsonToBean(String gsonString, Class cls) { 20 | T t = null; 21 | if (gson != null) { 22 | t = gson.fromJson(gsonString, cls); 23 | } 24 | return t; 25 | } 26 | 27 | public static String beanToJson(Object object) { 28 | String gsonString = null; 29 | if (gson != null) { 30 | gsonString = gson.toJson(object); 31 | } 32 | return gsonString; 33 | } 34 | 35 | /** 36 | * 转成list 37 | * 解决泛型问题 38 | * 39 | * @param json json 40 | * @param cls 类 41 | * @param T 42 | * @return T列表 43 | */ 44 | public static List jsonToList(String json, Class cls) { 45 | Gson gson = new Gson(); 46 | List list = new ArrayList<>(); 47 | JsonArray array = new JsonParser().parse(json).getAsJsonArray(); 48 | for (final JsonElement elem : array) { 49 | list.add(gson.fromJson(elem, cls)); 50 | } 51 | return list; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/utils/KeyboardDownUtil.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.utils; 2 | 3 | import android.app.Instrumentation; 4 | 5 | import java.util.concurrent.Future; 6 | import java.util.concurrent.LinkedBlockingQueue; 7 | import java.util.concurrent.ThreadPoolExecutor; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | public class KeyboardDownUtil { 11 | 12 | static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(1,1,200L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); 13 | 14 | public static void keyDown(final int key) { 15 | threadPool.execute(new Runnable() { 16 | @Override 17 | public void run() { 18 | sendKeyDownUpSync(key); 19 | } 20 | }); 21 | } 22 | 23 | //必须运行在子线程 24 | private static void sendKeyDownUpSync(int key) { 25 | try { 26 | Instrumentation inst = new Instrumentation(); 27 | inst.sendKeyDownUpSync(key); 28 | }catch (java.lang.SecurityException e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/utils/LoopArrayList.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.utils; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * This List is extends from ArrayList 7 | * 继承自ArrayList 8 | *

9 | * When list size() > 0 and The User get(index) the object out of index for List from list,Don't throw IndexOutOfBoundsException 10 | * And restart get Value List from index = 0 11 | * 当数组长度大于0 12 | * 且用户取的索引出现索引越界时,不会抛出异常,会从重新索引0开始继续取 13 | *

14 | * Avoid index transgressions(Avoid IndexOutOfBoundsException) 15 | * 避免索引越界 16 | *

17 | * Pay attention to get index value 18 | * 注意取值逻辑 19 | * 20 | * 21 | * @author Damon Lee CN 22 | * @see ArrayList 23 | * @since 1.2 24 | */ 25 | public class LoopArrayList extends ArrayList { 26 | @Override 27 | public E get(int index) { 28 | if (size() != 0 && index >= size()) { 29 | // KEY POINT 30 | return super.get(index % size()); 31 | } else { 32 | return super.get(index); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/utils/SpUtils.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.utils; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | import android.text.TextUtils; 6 | 7 | import com.zhang.autotouch.bean.TouchPoint; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | /** 13 | * SharedPreferences 工具类 14 | */ 15 | public class SpUtils { 16 | 17 | /** 18 | * 保存在手机里面的文件名 19 | */ 20 | private static final String FILE_NAME = "share_date"; 21 | private static final String KEY_TOUCH_LIST = "touch_list"; 22 | 23 | /** 24 | * 保存数据的方法,我们需要拿到保存数据的具体类型,然后根据类型调用不同的保存方法 25 | * @param context 26 | * @param key 27 | * @param object 28 | */ 29 | public static void setParam(Context context , String key, Object object){ 30 | 31 | String type = object.getClass().getSimpleName(); 32 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE); 33 | SharedPreferences.Editor editor = sp.edit(); 34 | 35 | if("String".equals(type)){ 36 | editor.putString(key, (String)object); 37 | } 38 | else if("Integer".equals(type)){ 39 | editor.putInt(key, (Integer)object); 40 | } 41 | else if("Boolean".equals(type)){ 42 | editor.putBoolean(key, (Boolean)object); 43 | } 44 | else if("Float".equals(type)){ 45 | editor.putFloat(key, (Float)object); 46 | } 47 | else if("Long".equals(type)){ 48 | editor.putLong(key, (Long)object); 49 | } 50 | editor.apply(); 51 | } 52 | 53 | 54 | /** 55 | * 得到保存数据的方法,我们根据默认值得到保存的数据的具体类型,然后调用相对于的方法获取值 56 | * @param context 57 | * @param key 58 | * @param defaultObject 59 | * @return 60 | */ 61 | public static Object getParam(Context context , String key, Object defaultObject){ 62 | String type = defaultObject.getClass().getSimpleName(); 63 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, Context.MODE_PRIVATE); 64 | 65 | if("String".equals(type)){ 66 | return sp.getString(key, (String)defaultObject); 67 | } 68 | 69 | else if("Integer".equals(type)){ 70 | return sp.getInt(key, (Integer)defaultObject); 71 | } 72 | 73 | else if("Boolean".equals(type)){ 74 | return sp.getBoolean(key, (Boolean)defaultObject); 75 | } 76 | 77 | else if("Float".equals(type)){ 78 | return sp.getFloat(key, (Float)defaultObject); 79 | } 80 | 81 | else if("Long".equals(type)){ 82 | return sp.getLong(key, (Long)defaultObject); 83 | } 84 | 85 | return null; 86 | } 87 | 88 | /** 89 | * 清除所有数据 90 | * @param context 91 | */ 92 | public static void clear(Context context) { 93 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, 94 | Context.MODE_PRIVATE); 95 | SharedPreferences.Editor editor = sp.edit(); 96 | editor.clear().apply(); 97 | } 98 | 99 | /** 100 | * 清除指定数据 101 | * @param context 102 | */ 103 | public static void clearAll(Context context) { 104 | SharedPreferences sp = context.getSharedPreferences(FILE_NAME, 105 | Context.MODE_PRIVATE); 106 | SharedPreferences.Editor editor = sp.edit(); 107 | editor.remove("定义的键名"); 108 | editor.apply(); 109 | } 110 | 111 | public static void addTouchPoint(Context context, TouchPoint touchPoint) { 112 | List touchPoints = getTouchPoints(context); 113 | touchPoints.add(touchPoint); 114 | setTouchPoints(context, touchPoints); 115 | } 116 | 117 | public static void setTouchPoints(Context context, List touchPoints) { 118 | String string = GsonUtils.beanToJson(touchPoints); 119 | setParam(context, KEY_TOUCH_LIST, string); 120 | } 121 | 122 | public static List getTouchPoints(Context context) { 123 | String string = (String) getParam(context, KEY_TOUCH_LIST, ""); 124 | if (TextUtils.isEmpty(string)) { 125 | return new ArrayList<>(); 126 | } 127 | return GsonUtils.jsonToList(string, TouchPoint.class); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/utils/ToastUtil.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.utils; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.widget.Toast; 6 | 7 | public class ToastUtil { 8 | 9 | @SuppressLint("StaticFieldLeak") 10 | private static Context mContext; 11 | 12 | public static void init(Context context) { 13 | mContext = context.getApplicationContext(); 14 | } 15 | 16 | public static void show(String content) { 17 | Toast.makeText(mContext, content, Toast.LENGTH_SHORT).show(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/zhang/autotouch/utils/WindowUtils.java: -------------------------------------------------------------------------------- 1 | package com.zhang.autotouch.utils; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.content.Context; 6 | import android.content.ContextWrapper; 7 | import android.content.pm.ActivityInfo; 8 | import android.content.res.Resources; 9 | import android.content.res.TypedArray; 10 | import android.graphics.PixelFormat; 11 | import android.os.Build; 12 | import android.util.Log; 13 | import android.view.View; 14 | import android.view.ViewConfiguration; 15 | import android.view.ViewGroup; 16 | import android.view.Window; 17 | import android.view.WindowManager; 18 | 19 | import java.lang.reflect.Field; 20 | import java.lang.reflect.Method; 21 | 22 | import static android.content.Context.WINDOW_SERVICE; 23 | import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 24 | 25 | /** 26 | * 屏幕工具类 27 | */ 28 | public class WindowUtils { 29 | 30 | /** 31 | * 判断是否是透明背景的Activity 32 | * @param context 33 | * @return 34 | */ 35 | public static boolean isTranslucentOrFloating(Context context){ 36 | boolean isTranslucentOrFloating = false; 37 | try { 38 | int [] styleableRes = (int[]) Class.forName("com.android.internal.R$styleable").getField("Window").get(null); 39 | final TypedArray ta = context.obtainStyledAttributes(styleableRes); 40 | Method m = ActivityInfo.class.getMethod("isTranslucentOrFloating", TypedArray.class); 41 | m.setAccessible(true); 42 | isTranslucentOrFloating = (boolean)m.invoke(null, ta); 43 | m.setAccessible(false); 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | } 47 | return isTranslucentOrFloating; 48 | } 49 | 50 | /** 51 | * 修复8.0以上透明背景Activity指定方向时候闪退的bug 52 | * @param activity 53 | */ 54 | public static void fixOrientation(Activity activity){ 55 | try { 56 | Field field = Activity.class.getDeclaredField("mActivityInfo"); 57 | field.setAccessible(true); 58 | ActivityInfo o = (ActivityInfo)field.get(activity); 59 | o.screenOrientation = -1; 60 | field.setAccessible(false); 61 | } catch (Exception e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | 66 | /** 67 | * 获取屏幕宽度 68 | */ 69 | public static int getScreenWidth(Context context){ 70 | return context.getResources().getDisplayMetrics().widthPixels; 71 | } 72 | 73 | /** 74 | * 获取屏幕高度 75 | */ 76 | public static int getScreenHeight(Context context){ 77 | return context.getResources().getDisplayMetrics().heightPixels; 78 | } 79 | 80 | /** 81 | * 获取虚拟按键高度 82 | * @param context 83 | * @return 84 | */ 85 | public static int getNavigationBarHeight(Context context) { 86 | int result = 0; 87 | if (hasNavBar(context)) { 88 | Resources res = context.getResources(); 89 | int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android"); 90 | if (resourceId > 0) { 91 | result = res.getDimensionPixelSize(resourceId); 92 | } 93 | } 94 | return result; 95 | } 96 | 97 | /** 98 | * 检查是否存在虚拟按键栏 99 | * @param context 100 | */ 101 | @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) 102 | public static boolean hasNavBar(Context context) { 103 | Resources res = context.getResources(); 104 | int resourceId = res.getIdentifier("config_showNavigationBar", "bool", "android"); 105 | if (resourceId != 0) { 106 | boolean hasNav = res.getBoolean(resourceId); 107 | // check override flag 108 | String sNavBarOverride = getNavBarOverride(); 109 | if ("1".equals(sNavBarOverride)) { 110 | hasNav = false; 111 | } else if ("0".equals(sNavBarOverride)) { 112 | hasNav = true; 113 | } 114 | return hasNav; 115 | } else { // fallback 116 | return !ViewConfiguration.get(context).hasPermanentMenuKey(); 117 | } 118 | } 119 | 120 | /** 121 | * 判断虚拟按键栏是否重写 122 | * 123 | * @return 124 | */ 125 | private static String getNavBarOverride() { 126 | String sNavBarOverride = null; 127 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 128 | try { 129 | Class c = Class.forName("android.os.SystemProperties"); 130 | Method m = c.getDeclaredMethod("get", String.class); 131 | m.setAccessible(true); 132 | sNavBarOverride = (String) m.invoke(null, "qemu.hw.mainkeys"); 133 | } catch (Throwable e) { 134 | } 135 | } 136 | return sNavBarOverride; 137 | } 138 | 139 | /** 140 | * 全屏 - 隐藏状态栏和虚拟按键 141 | * @param window 142 | */ 143 | public static void setHideVirtualKey(Window window) { 144 | //保持布局状态 145 | int uiOptions = View.SYSTEM_UI_FLAG_LAYOUT_STABLE| 146 | //布局位于状态栏下方 147 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION| 148 | //全屏 149 | View.SYSTEM_UI_FLAG_FULLSCREEN| 150 | //隐藏导航栏 151 | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION| 152 | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN; 153 | if (Build.VERSION.SDK_INT >= 19){ 154 | uiOptions |= 0x00001000; 155 | }else{ 156 | uiOptions |= View.SYSTEM_UI_FLAG_LOW_PROFILE; 157 | } 158 | window.getDecorView().setSystemUiVisibility(uiOptions); 159 | } 160 | 161 | /** 162 | * 改变屏幕亮度 163 | */ 164 | /** * 设置页面的透明度 * @param bgAlpha 1表示不透明 */ 165 | public static void setBackgroundAlpha(Activity activity, float bgAlpha) { 166 | WindowManager.LayoutParams lp = activity.getWindow().getAttributes(); 167 | lp.alpha = bgAlpha; 168 | if (bgAlpha == 1) { 169 | //不移除该Flag的话,在有视频的页面上的视频会出现黑屏的bug 170 | activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); 171 | 172 | } else { 173 | //此行代码主要是解决在华为手机上半透明效果无效的bug 174 | activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); 175 | } 176 | activity.getWindow().setAttributes(lp); 177 | } 178 | 179 | /** 180 | * 获取状态栏高度 181 | */ 182 | public static int getStatusBarHeight(Context context) { 183 | int statusBarHeight = 0; 184 | Resources res = context.getResources(); 185 | int resourceId = res.getIdentifier("status_bar_height", "dimen", "android"); 186 | if (resourceId > 0) { 187 | statusBarHeight = res.getDimensionPixelSize(resourceId); 188 | } 189 | return statusBarHeight; 190 | } 191 | 192 | /** 193 | * 将View全屏 194 | * @param context 195 | * @param view 196 | */ 197 | public static void enterFullScreen(Context context, View view){ 198 | //从原有的View中取出来 199 | ViewGroup parent = (ViewGroup) view.getParent(); 200 | if (parent != null) { 201 | parent.removeView(view); 202 | } 203 | 204 | //找到父布局 205 | ViewGroup contentView = scanForActivity(context) 206 | .findViewById(android.R.id.content); 207 | 208 | //添加到父布局中 209 | ViewGroup.LayoutParams params = new ViewGroup.LayoutParams( 210 | MATCH_PARENT,MATCH_PARENT); 211 | contentView.addView(view,params); 212 | } 213 | 214 | /** 215 | * 将View退出全屏 216 | */ 217 | public static void exitFullScreen(Context context, View view){ 218 | //找到父布局 219 | ViewGroup contentView = scanForActivity(context) 220 | .findViewById(android.R.id.content); 221 | contentView.removeView(view); 222 | } 223 | 224 | private static Activity scanForActivity(Context cont) { 225 | if (cont == null) { 226 | Log.d("scanForActivity","cont == null"); 227 | return null; 228 | } else if (cont instanceof Activity) { 229 | Log.d("scanForActivity","Activity"); 230 | return (Activity) cont; 231 | } else if (cont instanceof ContextWrapper) { 232 | Log.d("scanForActivity","ContextWrapper"); 233 | return scanForActivity(((ContextWrapper) cont).getBaseContext()); 234 | } 235 | Log.d("scanForActivity","not result"); 236 | return null; 237 | } 238 | 239 | public static WindowManager.LayoutParams newWmParams(int width, int height) { 240 | WindowManager.LayoutParams params = new WindowManager.LayoutParams(); 241 | params.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL 242 | | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON 243 | | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 244 | | WindowManager.LayoutParams.FLAG_SCALED 245 | | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR 246 | | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 247 | if (Build.VERSION.SDK_INT >= 26) { 248 | params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 249 | } else { 250 | params.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; 251 | } 252 | params.width = width; 253 | params.height = height; 254 | params.format = PixelFormat.TRANSLUCENT; 255 | return params; 256 | } 257 | 258 | public static WindowManager getWindowManager(Context context) { 259 | return (WindowManager) context.getSystemService(WINDOW_SERVICE); 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_bg_touch.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_bt_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_dialog_menu_bg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 22 | 23 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_add_point.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 25 | 26 | 33 | 34 | 45 | 46 | 57 | 58 | 70 | 71 | 83 | 84 | 91 | 92 | 98 | 99 | 100 | 112 | 113 |