Android實現(xiàn)自動點擊無障礙服務功能的實例代碼
ps: 不想看代碼的滑到最下面有apk包百度網(wǎng)盤下載地址
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' //協(xié)程 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 注冊權(quán)限和服務
<?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" //自動點擊事件 開啟/關(guān)閉 val ACTION_PLAY = "action_play" val ACTION_STOP = "action_stop" //關(guān)閉懸浮窗 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 } /** * 更新懸浮窗參數(shù) * * @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 //總是出現(xiàn)在應用程序窗口之上 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") } } /** * 跳轉(zhuǎn)設置開啟無障礙 */ private fun checkAccessibility() { val intent = Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS) startActivity(intent) } /** * 跳轉(zhuǎn)設置頂層懸浮窗 */ 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="先打開無障礙權(quán)限 和 懸浮窗頂層權(quán)限\n點擊位置在圖標左上角xy偏移一個px\n程序?qū)谘舆t一個間隔后開始執(zhí)行" 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="關(guān)閉懸浮視圖" 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
到此這篇關(guān)于Android實現(xiàn)自動點擊無障礙服務功能的文章就介紹到這了,更多相關(guān)android無障礙自動點擊內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Android實現(xiàn)知乎選項卡動態(tài)隱藏效果實例
選項卡相信對大家來說應該不陌生,最近發(fā)現(xiàn)知乎選項卡的動態(tài)隱藏效果不錯,下面這篇文章主要給大家介紹了關(guān)于Android實現(xiàn)知乎選項卡動態(tài)隱藏效果的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒,下面隨著小編來一起學習學習吧。2018-02-02百度語音識別(Baidu Voice) Android studio版本詳解
這篇文章主要介紹了百度語音識別(Baidu Voice) Android studio版本詳解的相關(guān)資料,需要的朋友可以參考下2016-09-09Android編程經(jīng)典代碼集錦(復制,粘貼,瀏覽器調(diào)用,Toast顯示,自定義Dialog等)
這篇文章主要介紹了Android編程經(jīng)典代碼集錦,包括Android的復制,粘貼,瀏覽器調(diào)用,Toast顯示,自定義Dialog等實現(xiàn)技巧,非常簡單實用,需要的朋友可以參考下2016-01-01Android 仿日歷翻頁、仿htc時鐘翻頁、數(shù)字翻頁切換效果
這篇文章主要介紹了Android 仿日歷翻頁、仿htc時鐘翻頁、數(shù)字翻頁切換效果,需要的朋友可以參考下2017-07-07