赞
踩
ps: 不想看代码的滑到最下面有apk包百度网盘下载地址
1. 先看效果图 不然都是耍流氓
2.项目目录
3.一些配置
build.gradle
- plugins {
- id 'com.android.application'
- id 'kotlin-android'
- id 'kotlin-android-extensions'
- }
-
- android {
- compileSdkVersion 31
- buildToolsVersion "30.0.3"
-
- defaultConfig {
- applicationId "com.znan.autoclick"
- minSdkVersion 24
- targetSdkVersion 31
- 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 无障碍服务
- class AutoClickService : AccessibilityService() {
-
- private val TAG = javaClass.canonicalName
-
- var mainScope: CoroutineScope? = null
-
- private val broadcastReceiver = BroadcastHandler(this)
-
- //点击间隔
- private var mInterval = -1L
-
- //点击坐标xy
- private var mPointX = -1f
- private var mPointY = -1f
-
- //悬浮窗视图
- private lateinit var mFloatingView: FloatingClickView
-
- companion object {
-
- //打开悬浮窗
- val ACTION_SHOW = "action_show"
-
- //自动点击事件 开启/关闭
- val ACTION_PLAY = "action_play"
- val ACTION_STOP = "action_stop"
-
- //关闭悬浮窗
- val ACTION_CLOSE = "action_close"
- }
-
- private inner class BroadcastHandler(val context: Context) : BroadcastReceiver() {
-
- fun register() {
- context.registerReceiver(
- this,
- IntentFilter().apply {
- addAction(BroadcastConstants.BROADCAST_ACTION_AUTO_CLICK)
- //息屏关闭自动点击事件
- addAction(Intent.ACTION_SCREEN_OFF)
- }
- )
- }
-
- fun unregister() {
- context.unregisterReceiver(this)
- }
-
- override fun onReceive(p0: Context?, intent: Intent?) {
- intent?.apply {
- when(action) {
- Intent.ACTION_SCREEN_OFF -> {
- mFloatingView.remove()
- mainScope?.cancel()
- }
- BroadcastConstants.BROADCAST_ACTION_AUTO_CLICK -> {
- when (getStringExtra(BroadcastConstants.KEY_ACTION)) {
- ACTION_SHOW -> {
- mFloatingView.remove()
- mainScope?.cancel()
- mInterval = getLongExtra(BroadcastConstants.KEY_INTERVAL, 5000)
- mFloatingView.show()
- }
- ACTION_PLAY -> {
- mPointX = getFloatExtra(BroadcastConstants.KEY_POINT_X, 0f)
- mPointY = getFloatExtra(BroadcastConstants.KEY_POINT_Y, 0f)
- mainScope = MainScope()
- autoClickView(mPointX, mPointY)
- }
- ACTION_STOP -> {
- mainScope?.cancel()
- }
- ACTION_CLOSE -> {
- mFloatingView.remove()
- mainScope?.cancel()
- }
- else -> {
- Log.e(TAG, "action error")
- }
- }
- }
- }
- }
- }
- }
-
- override fun onCreate() {
- super.onCreate()
- startForegroundNotification()
- mFloatingView = FloatingClickView(this)
- broadcastReceiver.register()
- }
-
- 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()
- broadcastReceiver.unregister()
- 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
- 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().apply {
- action = BroadcastConstants.BROADCAST_ACTION_AUTO_CLICK
- when (mCurrentState) {
- STATE_NORMAL -> {
- mCurrentState = STATE_CLICKING
- putExtra(BroadcastConstants.KEY_ACTION, AutoClickService.ACTION_PLAY)
- putExtra(BroadcastConstants.KEY_POINT_X, (location[0] - 1).toFloat())
- putExtra(BroadcastConstants.KEY_POINT_Y, (location[1] - 1).toFloat())
- ivIcon?.setImageResource(R.drawable.ic_auto_click_icon_green_24)
- }
- STATE_CLICKING -> {
- mCurrentState = STATE_NORMAL
- putExtra(BroadcastConstants.KEY_ACTION, AutoClickService.ACTION_STOP)
- ivIcon?.setImageResource(R.drawable.ic_auto_click_icon_gray_24)
- }
- }
- }
-
- context.sendBroadcast(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() {
- mCurrentState = STATE_NORMAL
- ivIcon?.setImageResource(R.drawable.ic_auto_click_icon_gray_24)
- mWindowManager.removeView(mView)
- }
-
- }
页面事件
MainActivity.kt
- 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()
- startAutoClickService()
- }
-
- 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(et_interval.text.toString().toLong())
- }
-
- 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 startAutoClickService() {
- val intent = Intent(this, AutoClickService::class.java)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- startForegroundService(intent)
- } else {
- startService(intent)
- }
- }
-
- private fun showFloatingWindow(interval: Long) {
- sendBroadcast(Intent().apply {
- action = BroadcastConstants.BROADCAST_ACTION_AUTO_CLICK
- putExtra(BroadcastConstants.KEY_ACTION, AutoClickService.ACTION_SHOW)
- putExtra(BroadcastConstants.KEY_INTERVAL, interval)
- })
- }
-
- private fun closeFloatWindow() {
- sendBroadcast(Intent().apply {
- action = BroadcastConstants.BROADCAST_ACTION_AUTO_CLICK
- putExtra(BroadcastConstants.KEY_ACTION, AutoClickService.ACTION_CLOSE)
- })
- }
-
-
- 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"
- }
-
- object BroadcastConstants {
- val BROADCAST_ACTION_AUTO_CLICK = "BROADCAST_ACTION_AUTO_CLICK"
-
- val KEY_ACTION = "KEY_ACTION"
- val KEY_INTERVAL = "KEY_INTERVAL"
- val KEY_POINT_X = "KEY_POINT_X"
- val KEY_POINT_Y = "KEY_POINT_Y"
- }
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.app-auto-click.zip
提取码: 6p2u
告辞~
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。