Android实现自动点击无障碍服务功能的实例代码
作者:请叫我公子
这篇文章主要介绍了Android实现自动点击无障碍服务功能,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
ps: 不想看代码的滑到最下面有apk包百度网盘下载地址
1. 先看效果图 不然都是耍流氓
2.项目目录
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
到此这篇关于Android实现自动点击无障碍服务功能的文章就介绍到这了,更多相关android无障碍自动点击内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!