Android实现自动点击无障碍服务功能的实例代码

作者:请叫我公子 时间:2022-08-31 12:14:26 

ps: 不想看代码的滑到最下面有apk包百度网盘下载地址

1. 先看效果图 不然都是耍流氓

Android实现自动点击无障碍服务功能的实例代码

2.项目目录

Android实现自动点击无障碍服务功能的实例代码

3.一些配置

build.gradle


plugins {
   id 'com.android.application'
   id 'kotlin-android'
   id 'kotlin-android-extensions'
}

android {
   compileSdkVersion 30
   buildToolsVersion "30.0.3"

defaultConfig {
       applicationId "com.znan.autoclick"
       minSdkVersion 24
       targetSdkVersion 30
       versionCode 1
       versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
   }

buildTypes {
       release {
           minifyEnabled false
           proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
       }
   }
   compileOptions {
       sourceCompatibility JavaVersion.VERSION_1_8
       targetCompatibility JavaVersion.VERSION_1_8
   }
   kotlinOptions {
       jvmTarget = '1.8'
   }
}

dependencies {

implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
   implementation 'androidx.core:core-ktx:1.3.2'
   implementation 'androidx.appcompat:appcompat:1.2.0'
   implementation 'com.google.android.material:material:1.3.0'
   implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
   testImplementation 'junit:junit:4.+'
   androidTestImplementation 'androidx.test.ext:junit:1.1.2'
   androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

//协程
   implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
   implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3"
}

accessibility.xml


<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
   android:accessibilityEventTypes="typeAllMask"
   android:accessibilityFeedbackType="feedbackAllMask"
   android:canPerformGestures="true"
   android:canRetrieveWindowContent="true"
   android:description="@string/accessibility_desc" />

AndroidManifest.xml 注册权限和服务


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.znan.autoclick">

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
   <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

<application
       android:allowBackup="true"
       android:icon="@mipmap/ic_launcher"
       android:label="@string/app_name"
       android:roundIcon="@mipmap/ic_launcher_round"
       android:supportsRtl="true"
       android:theme="@style/Theme.AutoClick">

<activity android:name=".MainActivity">
           <intent-filter>
               <action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
           </intent-filter>
       </activity>

<service android:name=".AutoClickService"
           android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
           <intent-filter>
               <action android:name="android.accessibilityservice.AccessibilityService" />
           </intent-filter>
           <meta-data
               android:name="android.accessibilityservice"
               android:resource="@xml/accessibility" />
       </service>
   </application>

</manifest>

4.代码

AutoClickService.kt 无障碍服务


import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
import android.app.Notification
import android.content.Intent
import android.graphics.Path
import android.os.Build
import android.util.Log
import android.view.accessibility.AccessibilityEvent
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import kotlinx.coroutines.*

class AutoClickService : AccessibilityService() {

private val TAG = javaClass.canonicalName

var mainScope: CoroutineScope? = null

//点击间隔
   private var mInterval = -1L

//点击坐标xy
   private var mPointX = -1f
   private var mPointY = -1f

//悬浮窗视图
   private lateinit var mFloatingView: FloatingClickView

companion object {
       val FLAG_ACTION = "flag_action"

//打开悬浮窗
       val ACTION_SHOW = "action_show"

//自动点击事件 开启/关闭
       val ACTION_PLAY = "action_play"
       val ACTION_STOP = "action_stop"

//关闭悬浮窗
       val ACTION_CLOSE = "action_close"

}

override fun onCreate() {
       super.onCreate()
       startForegroundNotification()
       mFloatingView = FloatingClickView(this)
   }

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
       Log.d(TAG, "onStartCommand " + intent?.extras)
       intent?.apply {
           val action = getStringExtra(FLAG_ACTION)
           Log.d(TAG, "action " + action)
           when (action) {

ACTION_SHOW -> {
                   mInterval = getLongExtra("interval", 5000)
                   mFloatingView.show()
               }
               ACTION_PLAY -> {
                   mPointX = getFloatExtra("pointX", 0f)
                   mPointY = getFloatExtra("pointY", 0f)

mainScope = MainScope()
                   autoClickView(mPointX, mPointY)
               }
               ACTION_STOP -> {
                   mainScope?.cancel()
               }
               ACTION_CLOSE -> {
                   mFloatingView.remove()
                   mainScope?.cancel()
               }
               else -> {
                   Log.e(TAG, "action error")
               }
           }
       }
       return super.onStartCommand(intent, flags, startId)
   }

private fun startForegroundNotification() {
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
           val notificationBuilder =
               NotificationCompat.Builder(this, NotificationConstants.CHANNEL_ID)
           val notification = notificationBuilder.setOngoing(true)
               .setSmallIcon(R.mipmap.ic_launcher)
               .setCategory(Notification.CATEGORY_SERVICE)
               .build()
           startForeground(-1, notification)

} else {
           startForeground(-1, Notification())
       }
   }

@RequiresApi(Build.VERSION_CODES.N)
   private fun autoClickView(x: Float, y: Float) {

mainScope?.launch {
           while (true) {
               delay(mInterval)
               Log.d(TAG, "auto click x:$x  y:$y")
               val path = Path()
               path.moveTo(x, y)
               val gestureDescription = GestureDescription.Builder()
                   .addStroke(GestureDescription.StrokeDescription(path, 100L, 100L))
                   .build()
               dispatchGesture(
                   gestureDescription,
                   object : AccessibilityService.GestureResultCallback() {
                       override fun onCompleted(gestureDescription: GestureDescription?) {
                           super.onCompleted(gestureDescription)
                           Log.d(TAG, "自动点击完成")
                       }

override fun onCancelled(gestureDescription: GestureDescription?) {
                           super.onCancelled(gestureDescription)
                           Log.d(TAG, "自动点击取消")
                       }
                   },
                   null
               )
           }
       }
   }

override fun onInterrupt() {
   }

override fun onAccessibilityEvent(event: AccessibilityEvent?) {
   }

override fun onDestroy() {
       super.onDestroy()
       mainScope?.cancel()
   }
}

悬浮窗

SingletonHolder.kt


open class SingletonHolder<out T, in A>(creator: (A) -> T) {
   private var creator: ((A) -> T)? = creator
   @Volatile private var instance: T? = null

fun getInstance(arg: A): T {
       val i = instance
       if (i != null) {
           return i
       }

return synchronized(this) {
           val i2 = instance
           if (i2 != null) {
               i2
           } else {
               val created = creator!!(arg)
               instance = created
               creator = null
               created
           }
       }
   }
}

FloatingManager.kt


import android.content.Context
import android.view.View
import android.view.WindowManager

class FloatingManager private constructor(context: Context) {

//获得WindowManager对象
   private var mWindowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager

companion object : SingletonHolder<FloatingManager, Context>(::FloatingManager)

/**
    * 添加悬浮窗
    * @param view
    * @param params
    * @return
    */
   fun addView(view: View, params: WindowManager.LayoutParams): Boolean {
       try {
           mWindowManager.addView(view, params)
           return true
       } catch (e: Exception) {
           e.printStackTrace()
       }

return false
   }

/**
    * 移除悬浮窗
    *
    * @param view
    * @return
    */
   fun removeView(view: View): Boolean {
       try {
           mWindowManager.removeView(view)
           return true
       } catch (e: Exception) {
           e.printStackTrace()
       }

return false
   }

/**
    * 更新悬浮窗参数
    *
    * @param view
    * @param params
    * @return
    */
   fun updateView(view: View, params: WindowManager.LayoutParams): Boolean {
       try {
           mWindowManager.updateViewLayout(view, params)
           return true
       } catch (e: Exception) {
           e.printStackTrace()
       }

return false
   }
}

FloatingClickView.kt


import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.graphics.PixelFormat
import android.os.Build
import android.view.*
import android.widget.FrameLayout
import androidx.appcompat.widget.AppCompatImageView

class FloatingClickView(private val mContext: Context) : FrameLayout(mContext) {

private lateinit var mWindowManager: FloatingManager
   private var mParams: WindowManager.LayoutParams? = null

private lateinit var mView: View

//按下坐标
   private var mTouchStartX = -1f
   private var mTouchStartY = -1f

val STATE_CLICKING = "state_clicking"
   val STATE_NORMAL = "state_normal"
   private var mCurrentState = STATE_NORMAL

private var ivIcon: AppCompatImageView? = null

init {
       initView()
   }

private fun initView() {
       mView = LayoutInflater.from(context).inflate(R.layout.view_floating_click, null)
       ivIcon = mView.findViewById(R.id.iv_icon)
       mWindowManager = FloatingManager.getInstance(mContext)
       initListener()
   }

@SuppressLint("ClickableViewAccessibility")
   private fun initListener() {
       mView.setOnTouchListener { v, event ->
           when (event.action) {
               MotionEvent.ACTION_DOWN -> {
                   mTouchStartX = event.rawX
                   mTouchStartY = event.rawY
               }

MotionEvent.ACTION_MOVE -> {
                   mParams?.let {
                       it.x += (event.rawX - mTouchStartX).toInt()
                       it.y += (event.rawY - mTouchStartY).toInt()
                       mWindowManager.updateView(mView, it)
                   }
                   mTouchStartX = event.rawX
                   mTouchStartY = event.rawY
               }
           }
           false
       }

mView.setOnClickListener {

val location = IntArray(2)
           it.getLocationOnScreen(location)
           val intent = Intent(context, AutoClickService::class.java)
           when (mCurrentState) {
               STATE_NORMAL -> {
                   mCurrentState = STATE_CLICKING
                   intent.putExtra(AutoClickService.FLAG_ACTION, AutoClickService.ACTION_PLAY)
                   intent.putExtra("pointX", (location[0] - 1).toFloat())
                   intent.putExtra("pointY", (location[1] - 1).toFloat())
                   ivIcon?.setImageResource(R.drawable.ic_auto_click_icon_green_24)
               }
               STATE_CLICKING -> {
                   mCurrentState = STATE_NORMAL
                   intent.putExtra(AutoClickService.FLAG_ACTION, AutoClickService.ACTION_STOP)
                   ivIcon?.setImageResource(R.drawable.ic_auto_click_icon_gray_24)
               }
           }
           context.startService(intent)
       }
   }

fun show() {
       mParams = WindowManager.LayoutParams()
       mParams?.apply {
           gravity = Gravity.CENTER
           //总是出现在应用程序窗口之上
           type = if (Build.VERSION.SDK_INT >= 26) {
               WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
           } else {
               WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
           }
           //设置图片格式,效果为背景透明
           format = PixelFormat.RGBA_8888

flags =
               WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or
                       WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN or WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR or
                       WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH

width = LayoutParams.WRAP_CONTENT
           height = LayoutParams.WRAP_CONTENT
           if (mView.isAttachedToWindow) {
               mWindowManager.removeView(mView)
           }
           mWindowManager.addView(mView, this)
       }
   }

fun remove() {
       mWindowManager.removeView(mView)
   }

}

页面事件

MainActivity.kt


import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.provider.Settings
import android.text.TextUtils
import android.util.Log
import android.view.inputmethod.InputMethodManager
import android.widget.Toast
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

private val TAG = javaClass::class.java.canonicalName

override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)
       initNotification()
       initListener()
   }

private fun initListener() {
       btn_accessibility.setOnClickListener {
           checkAccessibility()
       }

btn_floating_window.setOnClickListener {
           checkFloatingWindow()
       }

btn_show_window.setOnClickListener {
           hideKeyboard()
           if (TextUtils.isEmpty(et_interval.text.toString())) {
               Snackbar.make(et_interval, "请输入间隔", Snackbar.LENGTH_SHORT).show()
               return@setOnClickListener
           }

showFloatingWindow()
       }

btn_close_window.setOnClickListener {
           closeFloatWindow()
       }

btn_test.setOnClickListener {
           Log.d(TAG, "btn_test on click")
       }

}

/**
    * 跳转设置开启无障碍
    */
   private fun checkAccessibility() {
       val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)
       startActivity(intent)
   }

/**
    * 跳转设置顶层悬浮窗
    */
   private fun checkFloatingWindow() {
       if (Build.VERSION.SDK_INT >= 23) {
           if (Settings.canDrawOverlays(this)) {
               Toast.makeText(this, "已开启", Toast.LENGTH_SHORT).show()
           } else {
               val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)
               startActivity(intent)
           }
       }
   }

private fun showFloatingWindow() {
       val intent = Intent(this, AutoClickService::class.java)
       intent.apply {
           putExtra(AutoClickService.FLAG_ACTION, AutoClickService.ACTION_SHOW)
           putExtra("interval", et_interval.text.toString().toLong())
       }
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
           startForegroundService(intent)
       } else {
           startService(intent)
       }
   }

private fun closeFloatWindow() {
       val intent = Intent(this, AutoClickService::class.java)
       intent.putExtra(AutoClickService.FLAG_ACTION, AutoClickService.ACTION_CLOSE)
       startService(intent)
   }

private fun initNotification() {
       //注册渠道id
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
           val name = NotificationConstants.CHANNEl_NAME
           val descriptionText = NotificationConstants.CHANNEL_DES
           val importance = NotificationManager.IMPORTANCE_DEFAULT
           val channel =
               NotificationChannel(NotificationConstants.CHANNEL_ID, name, importance).apply {
                   description = descriptionText
               }
           channel.enableLights(true)
           channel.lightColor = Color.GREEN
           // Register the channel with the system
           val notificationManager: NotificationManager =
               getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
           notificationManager.createNotificationChannel(channel)
       }
   }

override fun onDestroy() {
       val intent = Intent(this, AutoClickService::class.java)
       stopService(intent)
       super.onDestroy()
   }

//收起输入法
   fun hideKeyboard() {
       val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
       if (imm.isActive && currentFocus != null) {
           imm.hideSoftInputFromWindow(currentFocus!!.windowToken, InputMethodManager.HIDE_NOT_ALWAYS)
       }
   }
}

object NotificationConstants {
   val CHANNEL_ID = "auto_channel_id"

val CHANNEl_NAME ="Auto Click"

val CHANNEL_DES = "Auto Click Service"
}

5.布局


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="match_parent">

<androidx.appcompat.widget.AppCompatTextView
       android:id="@+id/tv_message"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginStart="50dp"
       android:layout_marginTop="20dp"
       android:text="先打开无障碍权限 和 悬浮窗顶层权限\n点击位置在图标左上角xy偏移一个px\n程序将会在延迟一个间隔后开始执行"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toTopOf="parent" />

<com.google.android.material.button.MaterialButton
       android:id="@+id/btn_accessibility"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginStart="50dp"
       android:layout_marginTop="20dp"
       android:text="无障碍选项"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@id/tv_message" />

<com.google.android.material.button.MaterialButton
       android:id="@+id/btn_floating_window"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginStart="20dp"
       android:text="悬浮窗选项"
       app:layout_constraintStart_toEndOf="@id/btn_accessibility"
       app:layout_constraintTop_toTopOf="@id/btn_accessibility" />

<androidx.appcompat.widget.AppCompatTextView
       android:id="@+id/tv_unit"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginEnd="50dp"
       android:text="ms"
       android:textSize="24sp"
       app:layout_constraintBottom_toBottomOf="@id/et_interval"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintTop_toTopOf="@id/et_interval" />

<androidx.appcompat.widget.AppCompatEditText
       android:id="@+id/et_interval"
       android:layout_width="0dp"
       android:layout_height="48dp"
       android:layout_marginStart="50dp"
       android:layout_marginTop="50dp"
       android:layout_marginEnd="10dp"
       android:hint="点击的间隔(建议大于100)(毫秒)"
       android:inputType="number"
       android:textColor="#FF0000"
       android:textSize="14sp"
       app:layout_constraintEnd_toStartOf="@id/tv_unit"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@id/btn_accessibility" />

<com.google.android.material.button.MaterialButton
       android:id="@+id/btn_show_window"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginTop="50dp"
       android:text="打开悬浮视图"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@id/et_interval" />

<com.google.android.material.button.MaterialButton
       android:id="@+id/btn_close_window"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginTop="10dp"
       android:text="关闭悬浮视图"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent"
       app:layout_constraintTop_toBottomOf="@id/btn_show_window" />

<com.google.android.material.button.MaterialButton
       android:id="@+id/btn_test"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:layout_marginBottom="100dp"
       android:text="测试点击按钮"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintEnd_toEndOf="parent"
       app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content">

<androidx.appcompat.widget.AppCompatImageView
       android:id="@+id/iv_icon"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:src="@drawable/ic_auto_click_icon_gray_24" />
</FrameLayout>

6.debug.apk

链接: https://pan.baidu.com/s/1ukuJofO3SOfdOw5vgfMG4g

提取码: va43

来源:https://blog.csdn.net/qq_30837235/article/details/115902248

标签:android,无障碍,自动点击
0
投稿

猜你喜欢

  • C# 使用SharpZipLib生成压缩包的实例代码

    2021-08-29 20:32:57
  • 一文详解Spring是怎么读取配置Xml文件的

    2023-07-30 00:27:37
  • Android LayoutInflater.inflate源码分析

    2022-06-06 17:02:05
  • SpringBoot使用AOP+注解实现简单的权限验证的方法

    2022-07-29 00:59:09
  • 详解JAVA中的for-each循环与迭代

    2022-05-11 12:29:14
  • C# 获取客户端IPv4地址的示例代码

    2022-09-12 12:39:34
  • c#中的浮点型转整形的舍取 四舍五入和银行家舍入实现代码

    2023-01-10 02:04:41
  • Redis缓存,泛型集合与json字符串的相互转换实例

    2021-07-18 16:57:15
  • ToStringBuilder类的一些心得

    2022-10-10 04:02:27
  • Java中数组转List的三种方法与对比分析

    2023-03-27 08:51:03
  • springmvc处理模型数据ModelAndView过程详解

    2022-01-27 05:50:22
  • Java监听器ActionListener与MouseListener的执行顺序说明

    2022-02-04 20:08:23
  • Java构造代码块,静态代码块原理与用法实例分析

    2023-11-03 09:03:45
  • WinForm天猫双11自动抢红包源码分享

    2023-02-09 16:23:19
  • 浅谈Java中Spring Boot的优势

    2022-12-25 17:36:52
  • 基于Java8 Stream API实现数据抽取收集

    2021-10-01 03:13:03
  • 批处理一键安装JDK/一键安装JRE和自动配置Java环境变量

    2023-11-29 05:28:30
  • Android实现简单时钟View的方法

    2022-10-21 18:36:00
  • java图片添加水印实例代码分享

    2022-06-10 04:06:03
  • C# Winform实现圆角无锯齿按钮

    2022-08-30 01:11:56
  • asp之家 软件编程 m.aspxhome.com