├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── mipmap-xxxhdpi │ │ │ └── icon_logo.png │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── themes.xml │ │ ├── drawable │ │ │ └── bg_tip_dialog.xml │ │ ├── xml │ │ │ ├── backup_rules.xml │ │ │ └── data_extraction_rules.xml │ │ ├── layout │ │ │ ├── content_main.xml │ │ │ ├── activity_main.xml │ │ │ ├── fragment_main.xml │ │ │ └── dialog_version_tip.xml │ │ └── navigation │ │ │ └── nav_graph.xml │ │ ├── java │ │ └── com │ │ │ └── lygttpod │ │ │ └── android │ │ │ └── auto │ │ │ ├── ktx │ │ │ ├── ContextKtx.kt │ │ │ └── LogUtils.kt │ │ │ ├── App.kt │ │ │ ├── MainFragment.kt │ │ │ ├── update │ │ │ ├── UpdateApp.kt │ │ │ └── VersionTipDialog.kt │ │ │ └── MainActivity.kt │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── auto-ad ├── .gitignore ├── consumer-rules.pro ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── lygttpod │ │ │ └── android │ │ │ └── auto │ │ │ └── ad │ │ │ ├── data │ │ │ ├── FilterKeywordData.kt │ │ │ └── FuckAdAppsData.kt │ │ │ ├── ktx │ │ │ ├── GsonExt.kt │ │ │ └── ContextExt.kt │ │ │ ├── FuckAdManager.kt │ │ │ ├── accessibility │ │ │ └── FuckADAccessibility.kt │ │ │ ├── ui │ │ │ ├── adapter │ │ │ │ └── AppConfigAdapter.kt │ │ │ └── FuckAdMainFragment.kt │ │ │ └── task │ │ │ └── FuckADTask.kt │ │ ├── res │ │ ├── values │ │ │ └── strings.xml │ │ ├── xml │ │ │ └── fuck_ad_accessible_service_config.xml │ │ └── layout │ │ │ ├── item_app.xml │ │ │ ├── dialog_filter_keyword.xml │ │ │ ├── fragment_fuck_ad_main.xml │ │ │ └── dialog_app_config.xml │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── auto-tools ├── .gitignore ├── consumer-rules.pro ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── lygttpod │ │ │ └── android │ │ │ └── auto │ │ │ └── tools │ │ │ ├── AutoTools.kt │ │ │ ├── manager │ │ │ ├── ContentManger.kt │ │ │ └── FloatManager.kt │ │ │ ├── ktx │ │ │ ├── IpAddress.kt │ │ │ └── ContextExt.kt │ │ │ ├── accessibility │ │ │ └── AutoToolsAccessibility.kt │ │ │ ├── utils │ │ │ └── SPUtils.kt │ │ │ ├── ui │ │ │ └── ToolsMainFragment.kt │ │ │ └── service │ │ │ └── AutoToolsService.kt │ │ ├── res │ │ ├── values │ │ │ └── strings.xml │ │ ├── xml │ │ │ └── tools_accessible_service_config.xml │ │ └── layout │ │ │ ├── widget_float_print.xml │ │ │ └── fragment_tools_main.xml │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── auto-wx ├── .gitignore ├── consumer-rules.pro ├── src │ └── main │ │ ├── java │ │ └── com │ │ │ └── lygttpod │ │ │ └── android │ │ │ └── auto │ │ │ └── wx │ │ │ ├── data │ │ │ ├── NodeInfo.kt │ │ │ └── WxUserInfo.kt │ │ │ ├── em │ │ │ ├── FriendStatus.kt │ │ │ └── CheckStatus.kt │ │ │ ├── ktx │ │ │ └── FormatExt.kt │ │ │ ├── page │ │ │ ├── IPage.kt │ │ │ ├── WXMinePage.kt │ │ │ ├── group │ │ │ │ ├── WXGroupManagerPage.kt │ │ │ │ ├── WXGroupInfoPage.kt │ │ │ │ ├── WXCreateGroupPage.kt │ │ │ │ └── WXGroupChatPage.kt │ │ │ ├── WXContactInfoPage.kt │ │ │ ├── WXChattingPage.kt │ │ │ ├── WXContactPage.kt │ │ │ ├── WXHomePage.kt │ │ │ ├── WXRemittancePage.kt │ │ │ └── WXHBPage.kt │ │ │ ├── version │ │ │ └── WeChatVersionSupport.kt │ │ │ ├── service │ │ │ └── WXAccessibility.kt │ │ │ ├── helper │ │ │ ├── FriendStatusHelper.kt │ │ │ ├── HBTaskHelper.kt │ │ │ └── TaskByGroupHelper.kt │ │ │ └── adapter │ │ │ └── FriendInfoAdapter.kt │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ └── colors.xml │ │ ├── layout │ │ │ ├── item_version.xml │ │ │ ├── item_friend.xml │ │ │ └── fragment_wx_main.xml │ │ └── xml │ │ │ └── wx_accessible_service_config.xml │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── accessibility ├── .gitignore ├── consumer-rules.pro ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── com │ │ └── android │ │ └── accessibility │ │ └── ext │ │ ├── FormatExt.kt │ │ ├── task │ │ ├── i │ │ │ └── ITaskTracker.kt │ │ ├── RetryTaskWithLog.kt │ │ └── RetryTask.kt │ │ ├── AsyncAccessibilityService.kt │ │ ├── acc │ │ ├── AccessibilityEventExt.kt │ │ ├── AccessibilityNodeActionExt.kt │ │ ├── PrintNodeInfo.kt │ │ ├── GestureExt.kt │ │ └── AccessibilityNodeInfoExt.kt │ │ ├── data │ │ └── NodeWrapper.kt │ │ └── ContextExt.kt ├── proguard-rules.pro └── build.gradle ├── we_chat_tools.jks ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── settings.gradle ├── README.md ├── gradle.properties ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /auto-ad/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /auto-tools/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /auto-wx/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /auto-wx/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /accessibility/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /accessibility/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /auto-tools/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /auto-ad/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -keep class com.lygttpod.android.auto.ad.data.** { *; } -------------------------------------------------------------------------------- /we_chat_tools.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lygttpod/AndroidAuto/HEAD/we_chat_tools.jks -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lygttpod/AndroidAuto/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/icon_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lygttpod/AndroidAuto/HEAD/app/src/main/res/mipmap-xxxhdpi/icon_logo.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | local.properties 10 | -------------------------------------------------------------------------------- /accessibility/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /auto-wx/src/main/java/com/lygttpod/android/auto/wx/data/NodeInfo.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.wx.data 2 | 3 | data class NodeInfo(val nodeText: String, val nodeId: String, val des: String) 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Android自动化 3 | Android自动化工具 4 | 新版本来袭(%s) 5 | 6 | -------------------------------------------------------------------------------- /auto-ad/src/main/java/com/lygttpod/android/auto/ad/data/FilterKeywordData.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.ad.data 2 | 3 | import android.os.Parcelable 4 | import kotlinx.parcelize.Parcelize 5 | 6 | @Parcelize 7 | data class FilterKeywordData(val list: MutableList) : Parcelable 8 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Aug 05 09:48:33 CST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/lygttpod/android/auto/ktx/ContextKtx.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.ktx 2 | 3 | import android.content.Context 4 | 5 | fun Context.dp2px(dipValue: Float): Int { 6 | val scale = this.resources.displayMetrics.density 7 | return (dipValue * scale + 0.5f).toInt() 8 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/bg_tip_dialog.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /auto-wx/src/main/java/com/lygttpod/android/auto/wx/em/FriendStatus.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.wx.em 2 | 3 | enum class FriendStatus(val status: String) { 4 | BLACK("被拉黑"), 5 | DELETE("被删除"), 6 | NORMAL("正常"), 7 | ACCOUNT_EXCEPTION("帐号异常"), 8 | UNKNOW("待检测"), 9 | } -------------------------------------------------------------------------------- /auto-wx/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 微信自动化 4 | 5 | 【微信自动化】 6 | 1、自动化检测好友关系,甄别是否被好友 拉黑、删除,或者好友账号异常等状态;\n2、微信自动抢红包;\n3、自动化过程不涉个人信息,请放心使用 7 | -------------------------------------------------------------------------------- /accessibility/src/main/java/com/android/accessibility/ext/FormatExt.kt: -------------------------------------------------------------------------------- 1 | package com.android.accessibility.ext 2 | 3 | fun String?.default(default: String = "") = if (this.isNullOrBlank()) default else this 4 | 5 | fun CharSequence?.default(default: String = "") = 6 | if (this.isNullOrBlank()) default else this.toString() -------------------------------------------------------------------------------- /app/src/main/java/com/lygttpod/android/auto/ktx/LogUtils.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.ktx 2 | import android.util.Log 3 | import com.lygttpod.android.auto.BuildConfig 4 | 5 | object LogUtils { 6 | private const val TAG = "AndroidAuto" 7 | fun log(log: String) { 8 | if (BuildConfig.DEBUG) { 9 | Log.d(TAG, log) 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /auto-ad/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 自动跳过广告 4 | 5 | 【自动跳过广告(FuckAD)】 6 | 使用说明 7 | 使用说明\n\n在无障碍服务中找到【自动跳过广告(FuckAD)】并开启;\n\n自动跳过启动页带有【跳过】或【关闭】字样的广告\n 8 | 9 | -------------------------------------------------------------------------------- /auto-wx/src/main/java/com/lygttpod/android/auto/wx/ktx/FormatExt.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.wx.ktx 2 | 3 | fun Long.formatTime(): String { 4 | val m = (this / 1000 / 60 % 60).toInt() 5 | val s = (this / 1000 % 60).toInt() 6 | val minutes = if (m < 10) "$m" else m.toString() 7 | val seconds = if (s < 10) "$s" else s.toString() 8 | return "${minutes}分${seconds}秒" 9 | } 10 | -------------------------------------------------------------------------------- /auto-tools/src/main/java/com/lygttpod/android/auto/tools/AutoTools.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.tools 2 | 3 | import android.content.Context 4 | 5 | 6 | val AppContext = AutoTools.appContext ?: error("请先在application中调用AutoTools.init(this)初始化") 7 | 8 | object AutoTools { 9 | 10 | var appContext: Context? = null 11 | fun init(context: Context) { 12 | this.appContext = context 13 | } 14 | } -------------------------------------------------------------------------------- /auto-ad/src/main/java/com/lygttpod/android/auto/ad/ktx/GsonExt.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.ad.ktx 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.reflect.TypeToken 5 | 6 | 7 | private val gson = Gson() 8 | 9 | fun fromJson(json: String): T { 10 | return gson.fromJson(json, object : TypeToken() {}.type) 11 | } 12 | 13 | fun String?.toObject(): T? { 14 | val json = this ?: return null 15 | return fromJson(json) 16 | } -------------------------------------------------------------------------------- /auto-tools/src/main/java/com/lygttpod/android/auto/tools/manager/ContentManger.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.tools.manager 2 | 3 | import androidx.lifecycle.MutableLiveData 4 | 5 | object ContentManger { 6 | 7 | var printContent: String? = null 8 | set(value) { 9 | field = value 10 | printContentLiveData.postValue(value) 11 | } 12 | 13 | var printContentLiveData: MutableLiveData = MutableLiveData() 14 | } -------------------------------------------------------------------------------- /auto-wx/src/main/java/com/lygttpod/android/auto/wx/data/WxUserInfo.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.wx.data 2 | 3 | import com.lygttpod.android.auto.wx.em.FriendStatus 4 | 5 | data class WxUserInfo( 6 | val nickName: String, 7 | val wxCode: String? = null, 8 | var status: FriendStatus = FriendStatus.UNKNOW 9 | ) { 10 | override fun toString(): String { 11 | return "nickName: $nickName → wxCode: $wxCode → status: ${status.status}" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /auto-ad/src/main/java/com/lygttpod/android/auto/ad/FuckAdManager.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.ad 2 | 3 | import android.content.Context 4 | import com.tencent.mmkv.MMKV 5 | 6 | 7 | val AppContext = FuckAdManager.appContext ?: error("请先在application中调用FuckAdManager.init(this)初始化") 8 | 9 | object FuckAdManager { 10 | 11 | var appContext: Context? = null 12 | fun init(context: Context) { 13 | this.appContext = context 14 | MMKV.initialize(context) 15 | } 16 | } -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF6200EE 7 | #FF6200EE 8 | #FF000000 9 | #FFFFFFFF 10 | #FF808080 11 | -------------------------------------------------------------------------------- /auto-wx/src/main/java/com/lygttpod/android/auto/wx/page/IPage.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.wx.page 2 | 3 | import kotlinx.coroutines.delay 4 | 5 | interface IPage { 6 | 7 | fun delayTime() = 500L 8 | 9 | fun pageClassName(): String 10 | 11 | fun pageTitleName(): String 12 | 13 | fun isMe(): Boolean 14 | 15 | suspend fun delayAction(delayMillis: Long = delayTime(), block: suspend () -> T): T { 16 | delay(delayMillis) 17 | return block() 18 | } 19 | } -------------------------------------------------------------------------------- /auto-wx/src/main/res/layout/item_version.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /auto-wx/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #FF6200EE 5 | #FF000000 6 | #FFFFFFFF 7 | 8 | #ff3030 9 | #00ff00 10 | #ee00ee 11 | #FF6200EE 12 | #FF000000 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/lygttpod/android/auto/App.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto 2 | 3 | import android.app.Application 4 | import com.lygttpod.android.auto.ad.FuckAdManager 5 | import com.lygttpod.android.auto.tools.AutoTools 6 | 7 | class App : Application() { 8 | 9 | companion object { 10 | private var instance: Application? = null 11 | fun instance() = instance!! 12 | } 13 | 14 | override fun onCreate() { 15 | super.onCreate() 16 | instance = this 17 | AutoTools.init(this) 18 | FuckAdManager.init(this) 19 | } 20 | } -------------------------------------------------------------------------------- /auto-ad/src/main/res/xml/fuck_ad_accessible_service_config.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | jcenter() 14 | maven { url 'https://jitpack.io' } 15 | maven { url "https://raw.githubusercontent.com/Pgyer/mvn_repo_pgyer/master" } 16 | } 17 | } 18 | rootProject.name = "AndroidAuto" 19 | include ':app' 20 | include ':accessibility' 21 | include ':auto-wx' 22 | include ':auto-tools' 23 | include ':auto-ad' 24 | -------------------------------------------------------------------------------- /auto-tools/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 布局节点速查器 4 | 5 | 【布局节点速查器】 6 | 使用说明 7 | 在无障碍服务中找到【布局节点速查器】并开启;\n\n 8 | APP中查看节点信息:\n\n 9 | 开启悬浮窗权限\n 10 | 点击【页面节点速查器】按钮显示出悬浮窗\n 11 | 点击悬浮窗按钮\n 12 | 回到APP中即可查看\n\n 13 | 电脑端浏览器中查看节点信息:\n\n 14 | 确保手机端和电脑端在一个局域网内\n 15 | 电脑浏览器中输入页面提示的IP地址\n 16 | 点击输入IP地址显示的网页上的按钮即可查看\n 17 | 18 | -------------------------------------------------------------------------------- /auto-tools/src/main/res/xml/tools_accessible_service_config.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /accessibility/src/main/java/com/android/accessibility/ext/task/i/ITaskTracker.kt: -------------------------------------------------------------------------------- 1 | package com.android.accessibility.ext.task.i 2 | 3 | 4 | /** 5 | * 任务执行链路跟踪 6 | */ 7 | interface ITaskTracker { 8 | 9 | /** 方法开始执行触发 */ 10 | fun onStart() {} 11 | 12 | /** 方法执行成功触发 */ 13 | fun onSuccess(result: T, executeDuration: Long, executeCount: Int) {} 14 | 15 | /** 方法执行异常触发 */ 16 | fun onError(error: Throwable, executeDuration: Long, executeCount: Int) {} 17 | 18 | /** 每次重试触发 */ 19 | fun onEach(currentCount: Int) {} 20 | 21 | private object EMPTY : ITaskTracker 22 | 23 | @Suppress("UNCHECKED_CAST") 24 | companion object { 25 | fun empty(): ITaskTracker = EMPTY as ITaskTracker 26 | } 27 | } -------------------------------------------------------------------------------- /auto-wx/src/main/res/xml/wx_accessible_service_config.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /auto-ad/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /auto-wx/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /accessibility/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /auto-tools/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /auto-wx/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 11 | 12 | 13 | 14 | 15 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /auto-ad/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## AndroidAuto 2 | Android自动化工具 3 | 4 | ### [下载demo](https://www.pgyer.com/androidAuto)体验 5 | 6 | ### PC端浏览器效果 7 | ![PC端展示效果](https://github.com/lygttpod/AndroidAuto/assets/11826777/f76e782b-4f9e-4de0-ba1d-0670f586ff5d) 8 | 9 | ### 相关文章介绍(按顺序发布) 10 | 11 | * 一、[【玩转Android自动化】开篇序言](https://juejin.cn/post/7266076520111276069) 12 | * 二、[【玩转Android自动化】布局节点速查器](https://juejin.cn/post/7266087487939035192) 13 | * 三、[【玩转Android自动化】小试牛刀](https://juejin.cn/post/7266326381649821755) 14 | * 四、[【玩转Android自动化】微信好友导出](https://juejin.cn/post/7266332124663185463) 15 | * 五、[【玩转Android自动化】微信好友状态检查(假转账方式)](https://juejin.cn/post/7268539925096464444) 16 | * 六、[【玩转Android自动化】微信好友状态检查(拉群方式)](https://juejin.cn/post/7268590610188976165) 17 | * 七、[【玩转Android自动化】微信自动抢红包](https://juejin.cn/post/7269032845786513463) 18 | * 八、[【玩转Android自动化】微信版本适配](https://juejin.cn/post/7269046568211202103) 19 | * 九、[【玩转Android自动化】自动跳过APP启动页广告](https://juejin.cn/post/7277799132119416890) 20 | -------------------------------------------------------------------------------- /auto-wx/src/main/java/com/lygttpod/android/auto/wx/em/CheckStatus.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.wx.em 2 | 3 | enum class CheckStatus(val status: String, val mark: String) { 4 | BLACK("被拉黑", "请确认你和他(她)的好友关系是否正常"), 5 | DELETE("被删除", "你不是收款方好友,对方添加你为好友后才能发起转账"), 6 | ACCOUNT_EXCEPTION("帐号异常", "对方微信号已被限制登录,为保障你的资金安全,暂时无法完成交易"), 7 | NORMAL("正常", "付款方式"), 8 | PWD_PAY("正常", "请输入支付密码"), 9 | NO_AUTH("自己未实名认证", "实名认证"), 10 | } 11 | 12 | fun CheckStatus?.toFriendStatus(): FriendStatus { 13 | return when (this) { 14 | CheckStatus.BLACK -> FriendStatus.BLACK 15 | CheckStatus.DELETE -> FriendStatus.DELETE 16 | CheckStatus.ACCOUNT_EXCEPTION -> FriendStatus.ACCOUNT_EXCEPTION 17 | CheckStatus.NORMAL -> FriendStatus.NORMAL 18 | CheckStatus.PWD_PAY -> FriendStatus.NORMAL 19 | CheckStatus.NO_AUTH -> FriendStatus.NORMAL 20 | else -> FriendStatus.UNKNOW 21 | } 22 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | -dontwarn com.pgyersdk.** 24 | -keep class com.pgyersdk.** { *; } 25 | -keep class com.pgyersdk.**$* { *; } -------------------------------------------------------------------------------- /auto-wx/src/main/java/com/lygttpod/android/auto/wx/version/WeChatVersionSupport.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.wx.version 2 | 3 | import java.lang.reflect.Proxy 4 | 5 | val wechatVersionArray by lazy { WeChatNodesImpl.values().map { it.version }.toList() } 6 | 7 | var currentWXVersion = wechatVersionArray[0] 8 | 9 | private val wechatNodesImplMap by lazy { WeChatNodesImpl.values().associateBy { it.version } } 10 | 11 | val currentWechatNodes: WeChatNodesImpl 12 | get() = wechatNodesImplMap[currentWXVersion] ?: error("未适配的微信版本, currentWXVersion: $currentWXVersion") 13 | 14 | inline fun nodeProxy(): Nodes { 15 | val nodesClass = Nodes::class.java 16 | val proxy = 17 | Proxy.newProxyInstance(nodesClass.classLoader, arrayOf(nodesClass)) { _, method, args -> 18 | val impl = currentWechatNodes 19 | method.invoke(impl, *args.orEmpty()) 20 | } 21 | return proxy as Nodes 22 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /auto-tools/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 14 | 15 | 16 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /auto-ad/src/main/java/com/lygttpod/android/auto/ad/accessibility/FuckADAccessibility.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.ad.accessibility 2 | 3 | import android.view.accessibility.AccessibilityEvent 4 | import com.android.accessibility.ext.AsyncAccessibilityService 5 | import com.lygttpod.android.auto.ad.task.FuckADTask 6 | 7 | class FuckADAccessibility : AsyncAccessibilityService() { 8 | 9 | companion object { 10 | var fuckADAccessibility: FuckADAccessibility? = null 11 | } 12 | 13 | override fun targetPackageName() = "" 14 | 15 | override fun onCreate() { 16 | super.onCreate() 17 | fuckADAccessibility = this 18 | } 19 | 20 | override fun onDestroy() { 21 | fuckADAccessibility = null 22 | super.onDestroy() 23 | } 24 | 25 | override fun asyncHandleAccessibilityEvent(event: AccessibilityEvent) { 26 | // Log.d("FuckADAccessibility", "asyncHandleAccessibilityEvent: ${event}") 27 | FuckADTask.fuckAD(event) 28 | } 29 | } -------------------------------------------------------------------------------- /auto-tools/src/main/java/com/lygttpod/android/auto/tools/ktx/IpAddress.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.tools.ktx 2 | 3 | import java.net.Inet4Address 4 | import java.net.NetworkInterface 5 | import java.net.SocketException 6 | 7 | fun getPhoneWifiIpAddress(): String? { 8 | try { 9 | val networkInterfaces = NetworkInterface.getNetworkInterfaces() 10 | while (networkInterfaces.hasMoreElements()) { 11 | val networkInterface = networkInterfaces.nextElement() 12 | val inetAddresses = networkInterface.inetAddresses 13 | while (inetAddresses.hasMoreElements()) { 14 | val inetAddress = inetAddresses.nextElement() 15 | if (!inetAddress.isLoopbackAddress && inetAddress is Inet4Address) { 16 | return inetAddress.getHostAddress() 17 | } 18 | } 19 | } 20 | } catch (e: SocketException) { 21 | e.printStackTrace() 22 | return null 23 | } 24 | 25 | return null 26 | } -------------------------------------------------------------------------------- /auto-tools/src/main/res/layout/widget_float_print.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 23 | -------------------------------------------------------------------------------- /accessibility/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.android.accessibility.ext' 8 | compileSdk 32 9 | 10 | defaultConfig { 11 | minSdk 26 12 | targetSdk 32 13 | 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | consumerProguardFiles "consumer-rules.pro" 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | kotlinOptions { 29 | jvmTarget = '1.8' 30 | } 31 | } 32 | 33 | dependencies { 34 | implementation 'androidx.core:core-ktx:1.7.0' 35 | implementation 'androidx.appcompat:appcompat:1.4.1' 36 | implementation 'com.google.android.material:material:1.5.0' 37 | implementation 'androidx.navigation:navigation-fragment-ktx:2.4.1' 38 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /accessibility/src/main/java/com/android/accessibility/ext/AsyncAccessibilityService.kt: -------------------------------------------------------------------------------- 1 | package com.android.accessibility.ext 2 | 3 | import android.accessibilityservice.AccessibilityService 4 | import android.util.Log 5 | import android.view.accessibility.AccessibilityEvent 6 | import java.util.concurrent.Executors 7 | 8 | abstract class AsyncAccessibilityService : AccessibilityService() { 9 | private val TAG = this::class.java.simpleName 10 | 11 | private val executors = Executors.newSingleThreadExecutor() 12 | 13 | abstract fun targetPackageName(): String 14 | 15 | abstract fun asyncHandleAccessibilityEvent(event: AccessibilityEvent) 16 | 17 | override fun onAccessibilityEvent(event: AccessibilityEvent?) { 18 | val accessibilityEvent = event ?: return 19 | executors.run { asyncHandleAccessibilityEvent(accessibilityEvent) } 20 | } 21 | 22 | override fun onInterrupt() { 23 | Log.d(TAG, "onInterrupt: ") 24 | } 25 | 26 | override fun onServiceConnected() { 27 | Log.d(TAG, "onServiceConnected: ") 28 | } 29 | 30 | override fun onDestroy() { 31 | Log.d(TAG, "onDestroy: ") 32 | super.onDestroy() 33 | } 34 | } -------------------------------------------------------------------------------- /auto-ad/src/main/res/layout/item_app.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | 20 | 27 | 28 | -------------------------------------------------------------------------------- /accessibility/src/main/java/com/android/accessibility/ext/acc/AccessibilityEventExt.kt: -------------------------------------------------------------------------------- 1 | package com.android.accessibility.ext.acc 2 | 3 | import android.view.accessibility.AccessibilityEvent 4 | 5 | 6 | /** 7 | * 窗口状态变化 8 | */ 9 | fun AccessibilityEvent.isWindowStateChanged() = 10 | eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED 11 | 12 | /** 13 | * 窗口内容变化 14 | */ 15 | fun AccessibilityEvent.isWindowContentChanged() = 16 | eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED 17 | 18 | /** 19 | * 通知变化 20 | */ 21 | fun AccessibilityEvent.isNotificationStateChanged() = 22 | eventType == AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED 23 | 24 | /** 25 | * 是否是点击事件 26 | */ 27 | fun AccessibilityEvent.isViewClicked(): Boolean = 28 | this.eventType == AccessibilityEvent.TYPE_VIEW_CLICKED 29 | 30 | /** 31 | * 是否是指定viewId的点击事件 32 | */ 33 | fun AccessibilityEvent.onViewClickedById(viewId: String): Boolean { 34 | return isViewClicked() && source?.viewIdResourceName == viewId 35 | } 36 | 37 | /** 38 | * 是否是指定viewId的点击事件 39 | */ 40 | fun AccessibilityEvent.onViewClickedByText(text: String): Boolean { 41 | return isViewClicked() && text.find { it.toString() == text } != null 42 | } 43 | -------------------------------------------------------------------------------- /auto-ad/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-parcelize' 5 | } 6 | 7 | android { 8 | namespace 'com.lygttpod.android.auto.ad' 9 | compileSdk 33 10 | 11 | defaultConfig { 12 | minSdk 24 13 | 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | consumerProguardFiles "consumer-rules.pro" 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | compileOptions { 25 | sourceCompatibility JavaVersion.VERSION_1_8 26 | targetCompatibility JavaVersion.VERSION_1_8 27 | } 28 | kotlinOptions { 29 | jvmTarget = '1.8' 30 | } 31 | buildFeatures { 32 | viewBinding true 33 | } 34 | } 35 | 36 | dependencies { 37 | implementation(project(':accessibility')) 38 | implementation 'androidx.core:core-ktx:1.9.0' 39 | implementation 'androidx.appcompat:appcompat:1.6.1' 40 | implementation 'com.google.android.material:material:1.9.0' 41 | implementation 'com.google.code.gson:gson:2.10.1' 42 | implementation 'com.tencent:mmkv:1.3.1' 43 | } -------------------------------------------------------------------------------- /auto-wx/src/main/java/com/lygttpod/android/auto/wx/service/WXAccessibility.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.wx.service 2 | 3 | import android.accessibilityservice.AccessibilityService 4 | import android.view.accessibility.AccessibilityEvent 5 | import androidx.lifecycle.MutableLiveData 6 | import com.android.accessibility.ext.AsyncAccessibilityService 7 | import com.lygttpod.android.auto.wx.helper.HBTaskHelper 8 | import java.util.concurrent.atomic.AtomicBoolean 9 | 10 | val wxAccessibilityServiceLiveData = MutableLiveData(null) 11 | val wxAccessibilityService: AccessibilityService? get() = wxAccessibilityServiceLiveData.value 12 | 13 | class WXAccessibility : AsyncAccessibilityService() { 14 | 15 | companion object { 16 | var isInWXApp = AtomicBoolean(false) 17 | } 18 | 19 | override fun targetPackageName() = "com.tencent.mm" 20 | 21 | override fun onCreate() { 22 | super.onCreate() 23 | wxAccessibilityServiceLiveData.value = this 24 | } 25 | 26 | override fun onDestroy() { 27 | wxAccessibilityServiceLiveData.value = null 28 | super.onDestroy() 29 | } 30 | 31 | override fun asyncHandleAccessibilityEvent(event: AccessibilityEvent) { 32 | HBTaskHelper.hbTask(event) 33 | } 34 | } -------------------------------------------------------------------------------- /auto-wx/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | } 5 | 6 | android { 7 | namespace 'com.lygttpod.android.auto.wx' 8 | compileSdk 33 9 | 10 | defaultConfig { 11 | 12 | minSdk 26 13 | targetSdk 33 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | consumerProguardFiles "consumer-rules.pro" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_1_8 27 | targetCompatibility JavaVersion.VERSION_1_8 28 | } 29 | kotlinOptions { 30 | jvmTarget = '1.8' 31 | } 32 | buildFeatures { 33 | viewBinding true 34 | } 35 | } 36 | 37 | dependencies { 38 | implementation(project(":accessibility")) 39 | implementation 'androidx.core:core-ktx:1.9.0' 40 | implementation 'androidx.appcompat:appcompat:1.6.1' 41 | implementation 'com.google.android.material:material:1.8.0' 42 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 43 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.5.1' 44 | implementation 'com.github.lygttpod:ShapeView:1.0.2' 45 | } -------------------------------------------------------------------------------- /app/src/main/res/navigation/nav_graph.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | 19 | 20 | 25 | 26 | 31 | -------------------------------------------------------------------------------- /accessibility/src/main/java/com/android/accessibility/ext/acc/AccessibilityNodeActionExt.kt: -------------------------------------------------------------------------------- 1 | package com.android.accessibility.ext.acc 2 | 3 | import android.os.Bundle 4 | import android.view.accessibility.AccessibilityNodeInfo 5 | 6 | /** 7 | * 点击事件 8 | */ 9 | fun AccessibilityNodeInfo?.click(): Boolean { 10 | this ?: return false 11 | return if (isClickable) { 12 | performAction(AccessibilityNodeInfo.ACTION_CLICK) 13 | } else { 14 | parent?.click() == true 15 | } 16 | } 17 | 18 | /** 19 | * 长按事件 20 | */ 21 | fun AccessibilityNodeInfo.longClick(): Boolean { 22 | return if (isClickable) { 23 | performAction(AccessibilityNodeInfo.ACTION_LONG_CLICK) 24 | } else { 25 | parent.longClick() 26 | } 27 | } 28 | 29 | /** 30 | * 输入内容 31 | */ 32 | fun AccessibilityNodeInfo.inputText(input: String): Boolean { 33 | val arguments = Bundle().apply { 34 | putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, input) 35 | } 36 | return performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments) 37 | } 38 | 39 | /** 40 | * 向下滚动 41 | */ 42 | fun AccessibilityNodeInfo.scrollBackward() = 43 | performAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD) 44 | 45 | /** 46 | * 向上滚动 47 | */ 48 | fun AccessibilityNodeInfo.scrollForward() = 49 | performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD) 50 | -------------------------------------------------------------------------------- /auto-tools/src/main/java/com/lygttpod/android/auto/tools/accessibility/AutoToolsAccessibility.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.tools.accessibility 2 | 3 | import android.view.accessibility.AccessibilityEvent 4 | import android.view.accessibility.AccessibilityNodeInfo 5 | import android.view.accessibility.AccessibilityWindowInfo.TYPE_SYSTEM 6 | import com.android.accessibility.ext.AsyncAccessibilityService 7 | 8 | class AutoToolsAccessibility : AsyncAccessibilityService() { 9 | 10 | companion object { 11 | var autoToolsAccessibility: AutoToolsAccessibility? = null 12 | 13 | var floatWindowPackageName: String? = null 14 | fun getRootWindowNodeInfo(): AccessibilityNodeInfo? { 15 | if (floatWindowPackageName.isNullOrBlank()) return autoToolsAccessibility?.rootInActiveWindow 16 | return autoToolsAccessibility?.windows?.filter { it.type == TYPE_SYSTEM } 17 | ?.lastOrNull { it.root?.packageName == floatWindowPackageName }?.root 18 | } 19 | } 20 | 21 | override fun targetPackageName() = "" 22 | 23 | override fun onCreate() { 24 | super.onCreate() 25 | autoToolsAccessibility = this 26 | } 27 | 28 | override fun onDestroy() { 29 | autoToolsAccessibility = null 30 | super.onDestroy() 31 | } 32 | 33 | override fun asyncHandleAccessibilityEvent(event: AccessibilityEvent) { 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /auto-tools/src/main/java/com/lygttpod/android/auto/tools/ktx/ContextExt.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.tools.ktx 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.content.Intent 6 | import android.content.Intent.FLAG_ACTIVITY_NEW_TASK 7 | import android.net.Uri 8 | import android.provider.Settings 9 | import com.lygttpod.android.auto.tools.manager.FloatManager 10 | import com.lygttpod.android.auto.tools.utils.SPUtils 11 | 12 | fun Context?.gotoOverlayPermission() { 13 | this ?: return 14 | val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) 15 | intent.data = Uri.parse("package:" + this.applicationContext.packageName) 16 | intent.addFlags(FLAG_ACTIVITY_NEW_TASK) 17 | this.startActivity(intent) 18 | } 19 | 20 | fun Context?.canDrawOverlays(): Boolean { 21 | this ?: return false 22 | return Settings.canDrawOverlays(this) 23 | } 24 | 25 | fun Context?.showPrintFloat(application: Application) { 26 | this ?: return 27 | if (canDrawOverlays()) { 28 | FloatManager.showPrintFloat(application) 29 | } else { 30 | gotoOverlayPermission() 31 | } 32 | } 33 | 34 | fun Context.getSpValue(key: String): String { 35 | return SPUtils.getString(this, this.packageName, key) ?: "" 36 | } 37 | 38 | fun Context.setSpValue(key: String, value: Any) { 39 | SPUtils.saveValue(this, this.packageName, key, value) 40 | } 41 | 42 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true 24 | android.enableJetifier=true -------------------------------------------------------------------------------- /auto-tools/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.library' 3 | id 'org.jetbrains.kotlin.android' 4 | id 'kotlin-kapt' 5 | } 6 | 7 | android { 8 | namespace 'com.lygttpod.android.auto.tools' 9 | compileSdk 33 10 | 11 | defaultConfig { 12 | minSdk 26 13 | targetSdk 33 14 | 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | consumerProguardFiles "consumer-rules.pro" 17 | } 18 | 19 | buildTypes { 20 | release { 21 | minifyEnabled false 22 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 | } 24 | } 25 | compileOptions { 26 | sourceCompatibility JavaVersion.VERSION_17 27 | targetCompatibility JavaVersion.VERSION_17 28 | } 29 | kotlinOptions { 30 | jvmTarget = '17' 31 | } 32 | buildFeatures { 33 | viewBinding true 34 | } 35 | } 36 | 37 | dependencies { 38 | 39 | implementation(project(':accessibility')) 40 | implementation 'androidx.core:core-ktx:1.9.0' 41 | implementation 'androidx.appcompat:appcompat:1.6.1' 42 | implementation 'com.google.android.material:material:1.9.0' 43 | implementation 'com.github.lygttpod:ShapeView:1.0.2' 44 | implementation 'com.github.getActivity:EasyWindow:10.3' 45 | implementation 'io.github.lygttpod.android-local-service:core:0.0.1' 46 | implementation 'io.github.lygttpod.android-local-service:annotation:0.0.1' 47 | kapt 'io.github.lygttpod.android-local-service:processor:0.0.1' 48 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 18 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 33 | 34 | 35 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /auto-ad/src/main/java/com/lygttpod/android/auto/ad/data/FuckAdAppsData.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.ad.data 2 | 3 | import android.os.Parcelable 4 | import android.util.Log 5 | import kotlinx.parcelize.Parcelize 6 | 7 | @Parcelize 8 | data class FuckAdApps(val fuckAd: FuckAd) : Parcelable 9 | 10 | @Parcelize 11 | data class FuckAd(val apps: MutableList) : Parcelable 12 | 13 | @Parcelize 14 | data class AdApp( 15 | val appName: String, 16 | val packageName: String, 17 | val launcher: String, 18 | var lastSkipSuccessTime: Long = 0, 19 | var enableCheck: Boolean = true,//是否允许检测,默认允许 20 | var adNode: ADNode = ADNode() 21 | ) : Parcelable { 22 | fun skipSuccess() { 23 | lastSkipSuccessTime = System.currentTimeMillis() 24 | } 25 | 26 | /** 27 | * 五秒内成功跳过再次启动就不处理了,目的是为了减少检测次数 28 | */ 29 | fun isSkipped(intervalTime: Long = 5_000): Boolean { 30 | val skipped = System.currentTimeMillis() - lastSkipSuccessTime <= intervalTime 31 | if (skipped) { 32 | Log.d( 33 | "FuckADTask", 34 | "isSkipped: ${intervalTime / 1000}秒内已经成功跳过一次广告,无需重复检测" 35 | ) 36 | } 37 | return skipped 38 | } 39 | 40 | fun actionMaxLength(): Int = adNode.actionMaxLength 41 | 42 | fun getNodeId(): String = adNode.id 43 | fun getMaxLength(): Int = adNode.actionMaxLength 44 | } 45 | 46 | @Parcelize 47 | data class ADNode( 48 | var action: String = "跳过,关闭", 49 | var actionMaxLength: Int = 5, 50 | var id: String = "" 51 | ) : Parcelable 52 | -------------------------------------------------------------------------------- /app/src/main/java/com/lygttpod/android/auto/MainFragment.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import androidx.navigation.fragment.findNavController 9 | import com.lygttpod.android.auto.databinding.FragmentMainBinding 10 | 11 | 12 | class MainFragment : Fragment() { 13 | 14 | private var _binding: FragmentMainBinding? = null 15 | 16 | private val binding get() = _binding!! 17 | 18 | override fun onCreateView( 19 | inflater: LayoutInflater, container: ViewGroup?, 20 | savedInstanceState: Bundle? 21 | ): View { 22 | 23 | _binding = FragmentMainBinding.inflate(inflater, container, false) 24 | return binding.root 25 | 26 | } 27 | 28 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 29 | super.onViewCreated(view, savedInstanceState) 30 | initListener() 31 | } 32 | 33 | private fun initListener() { 34 | binding.btnPrintTools.setOnClickListener { 35 | findNavController().navigate(R.id.ToolsMainFragment) 36 | } 37 | 38 | binding.btnWxAuto.setOnClickListener { 39 | findNavController().navigate(R.id.WxMainFragment) 40 | } 41 | 42 | binding.btnFuckAd.setOnClickListener { 43 | findNavController().navigate(R.id.FuckAdMainFragment) 44 | } 45 | } 46 | 47 | override fun onDestroyView() { 48 | super.onDestroyView() 49 | _binding = null 50 | } 51 | } -------------------------------------------------------------------------------- /accessibility/src/main/java/com/android/accessibility/ext/acc/PrintNodeInfo.kt: -------------------------------------------------------------------------------- 1 | package com.android.accessibility.ext.acc 2 | 3 | import android.util.Log 4 | import android.view.accessibility.AccessibilityNodeInfo 5 | import com.android.accessibility.ext.data.NodeWrapper 6 | import com.android.accessibility.ext.default 7 | 8 | fun AccessibilityNodeInfo?.printNodeInfo( 9 | prefix: String = "", 10 | isLast: Boolean = false, 11 | printContent: StringBuilder = StringBuilder(), 12 | simplePrint: Boolean = false 13 | ): String { 14 | val node = this ?: return printContent.toString() 15 | val nodeWrapper = NodeWrapper( 16 | className = node.className.default(), 17 | text = node.text.default(), 18 | id = node.viewIdResourceName.default(), 19 | description = node.contentDescription.default(), 20 | isClickable = node.isClickable, 21 | isScrollable = node.isScrollable, 22 | isEditable = node.isEditable, 23 | isSelected = node.isSelected, 24 | isChecked = node.isChecked, 25 | nodeInfo = node 26 | ) 27 | val marker = if (isLast) """\--- """ else "+--- " 28 | val currentPrefix = "$prefix$marker" 29 | val print = 30 | currentPrefix + if (simplePrint) nodeWrapper.toSimpleString() else nodeWrapper.toString() 31 | printContent.append("${print}\n") 32 | Log.d("printNodeInfo", print) 33 | 34 | val size = node.childCount 35 | if (size > 0) { 36 | val childPrefix = prefix + if (isLast) " " else "| " 37 | val lastChildIndex = size - 1 38 | for (index in 0 until size) { 39 | val isLastChild = index == lastChildIndex 40 | node.getChild(index).printNodeInfo(childPrefix, isLastChild, printContent, simplePrint) 41 | } 42 | } 43 | return printContent.toString() 44 | } -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 21 | 22 | 35 | -------------------------------------------------------------------------------- /auto-wx/src/main/java/com/lygttpod/android/auto/wx/page/WXMinePage.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.wx.page 2 | 3 | import android.util.Log 4 | import com.android.accessibility.ext.acc.findById 5 | import com.android.accessibility.ext.default 6 | import com.android.accessibility.ext.task.retryTaskWithLog 7 | import com.lygttpod.android.auto.wx.data.NodeInfo 8 | import com.lygttpod.android.auto.wx.data.WxUserInfo 9 | import com.lygttpod.android.auto.wx.service.wxAccessibilityService 10 | import com.lygttpod.android.auto.wx.version.nodeProxy 11 | 12 | 13 | object WXMinePage : IPage { 14 | 15 | private val TAG = WXMinePage::class.java.simpleName 16 | 17 | interface Nodes { 18 | val mineNickNameNode: NodeInfo 19 | val mineWxCodeNode: NodeInfo 20 | 21 | companion object : Nodes by nodeProxy() 22 | } 23 | 24 | override fun pageClassName() = "" 25 | 26 | override fun pageTitleName() = "我的页面" 27 | 28 | override fun isMe(): Boolean { 29 | return wxAccessibilityService?.findById(Nodes.mineNickNameNode.nodeId)?.text.default() 30 | .isNotBlank() 31 | } 32 | 33 | suspend fun getMyWxInfo(): WxUserInfo? { 34 | return retryTaskWithLog("获取【我的】页面的微信昵称和微信号") { 35 | val nickName = 36 | wxAccessibilityService?.findById(Nodes.mineNickNameNode.nodeId)?.text.default() 37 | val wxCode = 38 | wxAccessibilityService?.findById(Nodes.mineWxCodeNode.nodeId)?.text?.split("微信号:") 39 | ?.getOrNull(1).default().trim() 40 | 41 | if (nickName.isNotBlank() && wxCode.isNotBlank()) { 42 | Log.d(TAG, "我的微信昵称: $nickName 我的微信号:$wxCode") 43 | WxUserInfo(nickName, wxCode) 44 | } else { 45 | null 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /accessibility/src/main/java/com/android/accessibility/ext/task/RetryTaskWithLog.kt: -------------------------------------------------------------------------------- 1 | package com.android.accessibility.ext.task 2 | 3 | import android.util.Log 4 | import com.android.accessibility.ext.task.i.ITaskTracker 5 | import kotlinx.coroutines.CoroutineScope 6 | 7 | private const val TAG = "LogTracker" 8 | 9 | 10 | suspend fun retryTaskWithLog( 11 | taskName: String, 12 | timeOutMillis: Long = 10_000L, 13 | periodMillis: Long = 500L, 14 | predicate: suspend CoroutineScope.() -> T? 15 | ): T? { 16 | return try { 17 | retryTask(timeOutMillis, periodMillis, LogTracker(taskName), predicate) 18 | } catch (e: Exception) { 19 | null 20 | } 21 | } 22 | 23 | 24 | suspend fun retryCheckTaskWithLog( 25 | taskName: String, 26 | timeOutMillis: Long = 10_000, 27 | periodMillis: Long = 500, 28 | predicate: suspend CoroutineScope.() -> Boolean 29 | ): Boolean { 30 | return try { 31 | retryCheckTask(timeOutMillis, periodMillis, LogTracker(taskName), predicate) 32 | } catch (e: Exception) { 33 | e.printStackTrace() 34 | false 35 | } 36 | } 37 | 38 | 39 | class LogTracker(private val taskName: String) : ITaskTracker { 40 | 41 | override fun onStart() { 42 | Log.d(TAG, "【$taskName】开始执行") 43 | } 44 | 45 | override fun onEach(currentCount: Int) { 46 | Log.d(TAG, "【$taskName】第 $currentCount 次执行") 47 | } 48 | 49 | override fun onSuccess(result: T, executeDuration: Long, executeCount: Int) { 50 | Log.d(TAG, "【$taskName】任务执行成功,轮训总次数:${executeCount}, 耗时:$executeDuration ms") 51 | } 52 | 53 | override fun onError(error: Throwable, executeDuration: Long, executeCount: Int) { 54 | Log.d(TAG, "【$taskName】任务执行异常【${error.message}】,轮训总次数:${executeCount}, 耗时:$executeDuration ms") 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /accessibility/src/main/java/com/android/accessibility/ext/data/NodeWrapper.kt: -------------------------------------------------------------------------------- 1 | package com.android.accessibility.ext.data 2 | 3 | import android.view.accessibility.AccessibilityNodeInfo 4 | 5 | /** 6 | * 结点包装,方便查看打印 7 | */ 8 | data class NodeWrapper( 9 | var className: String, 10 | var text: String? = null, 11 | var id: String? = null, 12 | var description: String? = null, 13 | var isClickable: Boolean = false, 14 | var isScrollable: Boolean = false, 15 | var isEditable: Boolean = false, 16 | var isSelected: Boolean = false, 17 | var isChecked: Boolean = false, 18 | var nodeInfo: AccessibilityNodeInfo? = null 19 | ) { 20 | override fun toString() = 21 | "className = $className → text = $text → id = $id → description = $description → isClickable = $isClickable → isScrollable = $isScrollable → isEditable = $isEditable" 22 | 23 | fun toSimpleString(): String { 24 | val ss = StringBuilder() 25 | ss.append("className = $className") 26 | if (text.isNullOrBlank().not()) { 27 | ss.append(" → text = $text") 28 | } 29 | if (id.isNullOrBlank().not()) { 30 | ss.append(" → id = $id") 31 | } 32 | if (description.isNullOrBlank().not()) { 33 | ss.append(" → description = $description") 34 | } 35 | if (isClickable) { 36 | ss.append(" → isClickable = $isClickable") 37 | } 38 | if (isScrollable) { 39 | ss.append(" → isScrollable = $isScrollable") 40 | } 41 | if (isEditable) { 42 | ss.append(" → isEditable = $isEditable") 43 | } 44 | if (isSelected) { 45 | ss.append(" → isSelected = $isSelected") 46 | } 47 | if (isChecked) { 48 | ss.append(" → isChecked = $isChecked") 49 | } 50 | return ss.toString() 51 | } 52 | } -------------------------------------------------------------------------------- /auto-ad/src/main/java/com/lygttpod/android/auto/ad/ktx/ContextExt.kt: -------------------------------------------------------------------------------- 1 | package com.lygttpod.android.auto.ad.ktx 2 | 3 | import android.content.Context 4 | import android.content.Intent 5 | import android.content.pm.ResolveInfo 6 | import android.util.Log 7 | import com.lygttpod.android.auto.ad.AppContext 8 | import com.lygttpod.android.auto.ad.data.AdApp 9 | 10 | 11 | fun Context.loadAsset(fileName: String): String? { 12 | try { 13 | return assets.open(fileName).bufferedReader().use { it.readText() } 14 | } catch (e: Exception) { 15 | e.printStackTrace() 16 | } 17 | return null 18 | } 19 | 20 | @Suppress("DEPRECATION") 21 | fun Context.queryAllInstallApp(): MutableList { 22 | val packageManager = this.packageManager 23 | // 创建一个 Intent,用于查询所有启动的应用程序 24 | val intent = Intent(Intent.ACTION_MAIN, null) 25 | intent.addCategory(Intent.CATEGORY_LAUNCHER) 26 | // 使用 queryIntentActivities 获取所有匹配的应用列表 27 | val appInfoList = 28 | packageManager.queryIntentActivities(intent, 0) 29 | .filterNot { isSystemApp(it) || isSelf(it) } 30 | .map { 31 | val appName = it.loadLabel(packageManager).toString() 32 | val packageName = it.activityInfo.packageName 33 | val className = it.activityInfo.name 34 | AdApp(appName, packageName, className) 35 | } 36 | .toMutableList() 37 | 38 | return appInfoList 39 | } 40 | 41 | // 检查应用是否为系统应用 42 | private fun isSystemApp(resolveInfo: ResolveInfo): Boolean { 43 | val regex = "com\\.android\\.[^.]+" // 匹配系统应用 com.android. 后面跟着任意不包含 . 的字符 44 | val result = resolveInfo.activityInfo.packageName.matches(Regex(regex)) 45 | Log.d("isSystemApp", "isSystemApp: ${resolveInfo.activityInfo.packageName} : $result") 46 | return result 47 | } 48 | 49 | private fun isSelf(resolveInfo: ResolveInfo): Boolean { 50 | return resolveInfo.activityInfo.packageName == AppContext.packageName 51 | } 52 | -------------------------------------------------------------------------------- /accessibility/src/main/java/com/android/accessibility/ext/task/RetryTask.kt: -------------------------------------------------------------------------------- 1 | package com.android.accessibility.ext.task 2 | 3 | import com.android.accessibility.ext.task.i.ITaskTracker 4 | import kotlinx.coroutines.CoroutineScope 5 | import kotlinx.coroutines.delay 6 | import kotlinx.coroutines.isActive 7 | import kotlinx.coroutines.withTimeout 8 | 9 | 10 | suspend fun retryCheckTask( 11 | timeOutMillis: Long = 5_000, 12 | periodMillis: Long = 500, 13 | tracker: ITaskTracker = ITaskTracker.empty(), 14 | predicate: suspend CoroutineScope.() -> Boolean 15 | ): Boolean { 16 | return try { 17 | val block: suspend CoroutineScope.() -> Unit? = { if (predicate()) Unit else null } 18 | retryTask(timeOutMillis, periodMillis, tracker, block) 19 | true 20 | } catch (e: Exception) { 21 | false 22 | } 23 | } 24 | 25 | suspend fun retryTask( 26 | timeOutMillis: Long = 5_000, 27 | periodMillis: Long = 500, 28 | tracker: ITaskTracker = ITaskTracker.empty(), 29 | block: suspend CoroutineScope.() -> T? 30 | ): T { 31 | val start = System.currentTimeMillis() 32 | var count = 0 33 | try { 34 | return withTimeout(timeOutMillis) { 35 | tracker.onStart() 36 | var result: T? = null 37 | while (isActive) { 38 | count++ 39 | result = block() 40 | if (null != result) { 41 | val executeDuration = System.currentTimeMillis() - start 42 | tracker.onSuccess(result, executeDuration, count) 43 | break 44 | } else { 45 | tracker.onEach(count) 46 | delay(periodMillis) 47 | } 48 | } 49 | result!! 50 | } 51 | } catch (e: Exception) { 52 | val executeDuration = System.currentTimeMillis() - start 53 | tracker.onError(e, executeDuration, count) 54 | throw e 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 |