Android实现自动点击 - 无障碍服务

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

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

Android实现自动点击 - 无障碍服务_第1张图片

 

2.项目目录

Android实现自动点击 - 无障碍服务_第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


AndroidManifest.xml 注册权限和服务




    
    

    

        
            
                

                
            
        

        
            
                
            
            
        
    

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(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)


    /**
     * 添加悬浮窗
     * @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.布局




    

    

    

    

    

    

    

    




    

6.debug.apk

https://pan.baidu.com/s/1fIkGZG7ToYk8HzwwtKN7Jw

 提取码: 2jhq 

 告辞~

你可能感兴趣的:(android,android,自动点击,无障碍)