├── .gitignore ├── .idea └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ └── com │ │ └── ypp │ │ └── adskip │ │ ├── AccessService.java │ │ ├── AccessUtils.java │ │ ├── ActionSettingActivity.java │ │ ├── AppListAdapter.java │ │ ├── AppNodeInfo.java │ │ ├── AppsSettingActivity.java │ │ ├── BootBroadcastReceiver.java │ │ ├── ExecuteIntentService.java │ │ ├── MainActivity.java │ │ └── Utils.java │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ ├── anchor.png │ ├── button_circle.xml │ ├── dialog_background.xml │ ├── help_icon.png │ ├── ic_launcher_background.xml │ ├── icon.png │ ├── notify_icon.png │ ├── permission_no.png │ ├── permission_yes.png │ ├── search_box_edittext_keyword_bg.9.png │ ├── timg.jpg │ ├── toggle_button_active.xml │ ├── toggle_button_active_press.xml │ ├── toggle_button_inactive.xml │ └── toggle_button_inactive_press.xml │ ├── layout │ ├── activity_action_setting.xml │ ├── activity_apps_setting.xml │ ├── activity_main.xml │ ├── app_list_item.xml │ ├── dialog_choose.xml │ ├── dialog_descripton.xml │ ├── notification_layout.xml │ └── view_anchor.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ ├── ic_launcher.png │ ├── ic_launcher_foreground.png │ └── ic_launcher_round.png │ ├── values │ ├── colors.xml │ ├── ic_launcher_background.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ └── accessible_service_config.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ADSkip 2 | ## 使用方式 3 | 打开软件,按照提示授予无障碍及其它防杀后台权限后,点击开启服务按钮即可使用 4 | ## 功能介绍 5 | 自动点击其它软件在开屏广告界面的跳过按钮,避免手点广告的麻烦 6 | 可单独对每个应用设置不点击、控件点击、屏幕点击和自定义点击四种方式 7 | 可自定义的内容包括点击位置和延时(打开软件延时多久后对对应位置点击) -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 29 5 | buildToolsVersion "29.0.0" 6 | defaultConfig { 7 | applicationId "com.ypp.adskip" 8 | minSdkVersion 26 9 | targetSdkVersion 29 10 | versionCode 1 11 | versionName "1.3.1" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(dir: 'libs', include: ['*.jar']) 24 | implementation 'androidx.appcompat:appcompat:1.1.0' 25 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 26 | testImplementation 'junit:junit:4.12' 27 | androidTestImplementation 'androidx.test.ext:junit:1.1.1' 28 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 29 | implementation 'com.google.android.material:material:1.1.0' 30 | } 31 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 17 | 18 | 19 | 20 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 35 | 36 | 37 | 38 | 39 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Promisin/ADSkip/13e00bcc789c760d0e3fb8602dd0196436862ef7/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/ypp/adskip/AccessService.java: -------------------------------------------------------------------------------- 1 | package com.ypp.adskip; 2 | 3 | import android.accessibilityservice.AccessibilityService; 4 | import android.app.Notification; 5 | import android.app.NotificationChannel; 6 | import android.app.NotificationManager; 7 | import android.app.PendingIntent; 8 | import android.content.Intent; 9 | import android.content.SharedPreferences; 10 | import android.util.Log; 11 | import android.view.accessibility.AccessibilityEvent; 12 | import android.view.accessibility.AccessibilityNodeInfo; 13 | import android.view.accessibility.AccessibilityWindowInfo; 14 | import android.widget.RemoteViews; 15 | 16 | import androidx.core.app.NotificationCompat; 17 | 18 | import java.util.List; 19 | 20 | 21 | public class AccessService extends AccessibilityService { 22 | private final String TAG = "AccessService"; 23 | private final String CHANNEL_ID = "keepAccessService"; 24 | private final String CHANNEL_NAME = "keepAccessService"; 25 | private final String CHANNEL_DESCRIPTION = "前台服务保活"; 26 | private final int NOTIFICATION_ID = 391; 27 | private static AccessService mAccessService; 28 | private NotificationCompat.Builder builder; 29 | private RemoteViews viewNoti; 30 | private NotificationManager manager; 31 | private SharedPreferences sharedPreferences; 32 | private boolean canStartWork = false; 33 | private boolean canConfirmPackage = false; 34 | private long lastClickTime; 35 | private String currentPackage; 36 | 37 | public AccessService() { 38 | mAccessService = this; 39 | } 40 | 41 | public static AccessService getServiceInstance(){ 42 | return mAccessService; 43 | } 44 | 45 | @Override 46 | public void onAccessibilityEvent(AccessibilityEvent event) { 47 | if (event.getEventType()==AccessibilityEvent.TYPE_VIEW_CLICKED){ 48 | Log.d(TAG, "onAccessibilityEvent: "+event.toString()); 49 | } 50 | if (canConfirmPackage){ 51 | currentPackage = event.getPackageName().toString(); 52 | canConfirmPackage = false; 53 | } 54 | if (event.getPackageName() != null && 55 | event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED && 56 | event.getPackageName().toString().contains("launcher") && 57 | !event.getPackageName().toString().equals(this.getPackageName())) { 58 | canStartWork = true; 59 | canConfirmPackage = true; 60 | lastClickTime = event.getEventTime(); 61 | } 62 | if (!canStartWork) { 63 | return; 64 | } 65 | Log.d(TAG, "onAccessibilityEvent: "+currentPackage); 66 | int actionFlag = Utils.ACTION_VIEW_CLICK; 67 | if (event.getPackageName()!=null && (actionFlag = sharedPreferences 68 | .getInt(currentPackage,Utils.ACTION_VIEW_CLICK))==Utils.ACTION_NO_CLICK){ 69 | canStartWork = false; 70 | return; 71 | } 72 | Log.d(TAG, "onAccessibilityEvent: "+actionFlag); 73 | try { 74 | if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED || 75 | event.getEventType() == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED && 76 | event.getEventTime()>lastClickTime) { 77 | //进入一个新的Activity 78 | List windows = getWindows(); 79 | AccessibilityNodeInfo rootInfo = null; 80 | for (AccessibilityWindowInfo info : windows){ 81 | Log.d(TAG, "onAccessibilityEvent: "+info.toString()); 82 | if (info.getType()==AccessibilityWindowInfo.TYPE_APPLICATION){ 83 | rootInfo = info.getRoot(); 84 | } 85 | } 86 | if (rootInfo!=null){ 87 | ExecuteIntentService.startExecuteInfo(getApplicationContext(), rootInfo, currentPackage, actionFlag); 88 | } 89 | if ((event.getEventTime()-lastClickTime)>3000 || actionFlag==Utils.ACTION_CUSTOM_CLICK){ 90 | canStartWork = false; 91 | } 92 | } 93 | } catch (Exception e) { 94 | e.printStackTrace(); 95 | canStartWork = false; 96 | } 97 | 98 | 99 | } 100 | 101 | @Override 102 | public void onInterrupt() { 103 | 104 | } 105 | 106 | @Override 107 | public void onCreate() { 108 | super.onCreate(); 109 | Log.d(TAG, "onCreate: "); 110 | Utils.setServiceRunning(getApplicationContext(),true); 111 | sharedPreferences = getApplicationContext().getSharedPreferences("app_action",MODE_PRIVATE); 112 | } 113 | 114 | @Override 115 | public int onStartCommand(Intent intent, int flags, int startId) { 116 | if (intent.getBooleanExtra("tryDisable",false)){ 117 | disableSelf(); 118 | } 119 | Log.d(TAG, "onStartCommand: "); 120 | NotificationChannel channel = new NotificationChannel( 121 | CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_MIN); 122 | channel.setDescription(CHANNEL_DESCRIPTION); 123 | channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); 124 | channel.setSound(null, null); 125 | channel.enableVibration(false); 126 | manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 127 | manager.createNotificationChannel(channel); 128 | viewNoti = new RemoteViews(getPackageName(), R.layout.notification_layout); 129 | builder = new NotificationCompat.Builder(this.getApplicationContext(), CHANNEL_ID); 130 | builder.setSmallIcon(R.drawable.notify_icon) 131 | .setContentIntent(PendingIntent.getActivity( 132 | this, 0, 133 | new Intent(this, MainActivity.class) 134 | .addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 135 | 0)) 136 | .setWhen(System.currentTimeMillis()) 137 | .setContent(viewNoti) 138 | .setPriority(NotificationCompat.PRIORITY_MIN); 139 | startForeground(NOTIFICATION_ID, builder.build()); 140 | return super.onStartCommand(intent, flags, startId); 141 | } 142 | 143 | @Override 144 | public void onDestroy() { 145 | super.onDestroy(); 146 | Log.d(TAG, "onDestroy: "); 147 | manager.cancel(NOTIFICATION_ID); 148 | Utils.setServiceRunning(getApplicationContext(),false); 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /app/src/main/java/com/ypp/adskip/AccessUtils.java: -------------------------------------------------------------------------------- 1 | package com.ypp.adskip; 2 | 3 | import android.accessibilityservice.GestureDescription; 4 | import android.content.ComponentName; 5 | import android.content.Context; 6 | import android.graphics.Path; 7 | import android.provider.Settings; 8 | import android.text.TextUtils; 9 | import android.util.Log; 10 | import android.view.ViewConfiguration; 11 | import android.view.accessibility.AccessibilityNodeInfo; 12 | 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | import static com.ypp.adskip.AccessService.getServiceInstance; 17 | 18 | class AccessUtils { 19 | private static final String TAG = "AccessUtils"; 20 | static void click(AccessibilityNodeInfo info) { 21 | if (info==null){ 22 | Log.d(TAG, "click: 不存在指定控件"); 23 | return; 24 | } 25 | if (info.isClickable()) { 26 | Log.d(TAG, "click: "+info.getViewIdResourceName()); 27 | info.performAction(AccessibilityNodeInfo.ACTION_CLICK); 28 | } else { 29 | click(info.getParent()); 30 | } 31 | } 32 | 33 | static void clickInScreen(int x, int y){ 34 | int duration = ViewConfiguration.getTapTimeout()+200; 35 | Path path = new Path(); 36 | path.moveTo(x, y); 37 | GestureDescription.StrokeDescription description = 38 | new GestureDescription.StrokeDescription(path, 0, duration); 39 | GestureDescription.Builder builder = new GestureDescription.Builder(); 40 | builder.addStroke(description); 41 | getServiceInstance().dispatchGesture(builder.build(), null, null); 42 | Log.d(TAG, "clickInScreen: "+x+" "+y); 43 | } 44 | 45 | static List findAccessibilityNodeInfosByText(AccessibilityNodeInfo rootNodeInfo, String targetString){ 46 | List resultList = new ArrayList<>(); 47 | if (rootNodeInfo!=null && rootNodeInfo.getChildCount()!=0){ 48 | for (int i = 0; i < rootNodeInfo.getChildCount(); i++) { 49 | AccessibilityNodeInfo childInfo = rootNodeInfo.getChild(i); 50 | if (childInfo!=null && 51 | childInfo.getText()!=null && 52 | childInfo.getText().toString().contains(targetString)) { 53 | resultList.add(childInfo); 54 | } 55 | resultList.addAll(findAccessibilityNodeInfosByText(childInfo, targetString)); 56 | if (childInfo!=null && !resultList.contains(childInfo)){ 57 | childInfo.recycle(); 58 | } 59 | } 60 | } 61 | return resultList; 62 | } 63 | 64 | static List findAccessibilityNodeInfosByIDContain(AccessibilityNodeInfo rootNodeInfo, String targetString){ 65 | List resultList = new ArrayList<>(); 66 | if (rootNodeInfo!=null && rootNodeInfo.getChildCount()!=0){ 67 | for (int i = 0; i < rootNodeInfo.getChildCount(); i++) { 68 | AccessibilityNodeInfo childInfo = rootNodeInfo.getChild(i); 69 | if (childInfo!=null && childInfo.getViewIdResourceName()!=null) { 70 | String[] resourceID = childInfo.getViewIdResourceName().split(":"); 71 | if (resourceID.length>=2 && resourceID[1].contains(targetString)){ 72 | resultList.add(childInfo); 73 | } 74 | } 75 | resultList.addAll(findAccessibilityNodeInfosByIDContain(childInfo, targetString)); 76 | if (childInfo!=null && !resultList.contains(childInfo)){ 77 | childInfo.recycle(); 78 | } 79 | } 80 | } 81 | return resultList; 82 | } 83 | 84 | static boolean isAccessibilityServiceEnabled(Context context, Class serviceClass){ 85 | ComponentName componentName = new ComponentName(context, serviceClass); 86 | String enabledService = Settings.Secure.getString(context.getContentResolver(), 87 | Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES); 88 | if (enabledService != null){ 89 | TextUtils.SimpleStringSplitter splitter = new TextUtils.SimpleStringSplitter(':'); 90 | splitter.setString(enabledService); 91 | 92 | while (splitter.hasNext()){ 93 | String name = splitter.next(); 94 | ComponentName currentComName =ComponentName.unflattenFromString(name); 95 | if (currentComName.equals(componentName)){ 96 | return true; 97 | } 98 | } 99 | } 100 | return false; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/com/ypp/adskip/ActionSettingActivity.java: -------------------------------------------------------------------------------- 1 | package com.ypp.adskip; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import androidx.constraintlayout.widget.ConstraintLayout; 5 | 6 | import android.content.Context; 7 | import android.content.SharedPreferences; 8 | import android.graphics.PixelFormat; 9 | import android.graphics.Rect; 10 | import android.graphics.RectF; 11 | import android.os.Build; 12 | import android.os.Bundle; 13 | import android.util.Log; 14 | import android.view.Gravity; 15 | import android.view.LayoutInflater; 16 | import android.view.MotionEvent; 17 | import android.view.View; 18 | import android.view.ViewConfiguration; 19 | import android.view.ViewGroup; 20 | import android.view.WindowManager; 21 | import android.widget.Button; 22 | import android.widget.CompoundButton; 23 | import android.widget.EditText; 24 | import android.widget.RadioButton; 25 | import android.widget.RadioGroup; 26 | import android.widget.TextView; 27 | import android.widget.Toast; 28 | 29 | import static com.ypp.adskip.Utils.ACTION_CUSTOM_CLICK; 30 | import static com.ypp.adskip.Utils.ACTION_NO_CLICK; 31 | import static com.ypp.adskip.Utils.ACTION_SCREEN_CLICK; 32 | import static com.ypp.adskip.Utils.ACTION_VIEW_CLICK; 33 | 34 | public class ActionSettingActivity extends AppCompatActivity { 35 | private final String TAG = "ActionSettingActivity"; 36 | private RadioGroup actionRg; 37 | private RadioButton customClickRb; 38 | private ConstraintLayout customLayout; 39 | private TextView xDisTv; 40 | private TextView yDisTv; 41 | private EditText delayEt; 42 | private Button saveButton; 43 | private TextView actionDescription; 44 | private SharedPreferences sharedPreferences; 45 | private SharedPreferences.Editor editor; 46 | private SharedPreferences positionPreferences; 47 | private SharedPreferences.Editor positionEditor; 48 | private SharedPreferences delayPreferences; 49 | private SharedPreferences.Editor delayEditor; 50 | private String packageName; 51 | private WindowManager windowManager; 52 | private View anchorView; 53 | 54 | @Override 55 | protected void onCreate(Bundle savedInstanceState) { 56 | super.onCreate(savedInstanceState); 57 | setContentView(R.layout.activity_action_setting); 58 | setTitle(R.string.action_setting_title); 59 | sharedPreferences = getApplicationContext() 60 | .getSharedPreferences("app_action", Context.MODE_PRIVATE); 61 | editor = sharedPreferences.edit(); 62 | positionPreferences = getApplicationContext() 63 | .getSharedPreferences("app_position",MODE_PRIVATE); 64 | positionEditor = positionPreferences.edit(); 65 | delayPreferences = getApplicationContext() 66 | .getSharedPreferences("app_delay",MODE_PRIVATE); 67 | delayEditor = delayPreferences.edit(); 68 | packageName = getIntent().getStringExtra("packageName"); 69 | actionRg = findViewById(R.id.action_rg); 70 | customClickRb = findViewById(R.id.custom_click_rb); 71 | actionDescription = findViewById(R.id.action_description); 72 | customLayout = findViewById(R.id.custom_layout); 73 | xDisTv = findViewById(R.id.x_dis_tv); 74 | yDisTv = findViewById(R.id.y_dis_tv); 75 | delayEt = findViewById(R.id.delay_et); 76 | saveButton = findViewById(R.id.save_button); 77 | actionRg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { 78 | @Override 79 | public void onCheckedChanged(RadioGroup group, int checkedId) { 80 | Log.d(TAG, "onCheckedChanged: " + checkedId); 81 | SharedPreferences.Editor editor = sharedPreferences.edit(); 82 | switch (checkedId) { 83 | case R.id.no_click_rb: 84 | editor.putInt(packageName, Utils.ACTION_NO_CLICK); 85 | actionDescription.setText(R.string.description_no_click); 86 | break; 87 | case R.id.view_click_rb: 88 | editor.putInt(packageName, Utils.ACTION_VIEW_CLICK); 89 | actionDescription.setText(R.string.description_view_click); 90 | break; 91 | case R.id.screen_click_rb: 92 | editor.putInt(packageName, Utils.ACTION_SCREEN_CLICK); 93 | actionDescription.setText(R.string.description_screen_click); 94 | break; 95 | case R.id.custom_click_rb: 96 | actionDescription.setText(R.string.description_custom_click); 97 | default: 98 | break; 99 | } 100 | editor.apply(); 101 | } 102 | }); 103 | customClickRb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 104 | @Override 105 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 106 | if (isChecked){ 107 | showAnchor(); 108 | xDisTv.setText(positionPreferences.getString(packageName+"x","")); 109 | yDisTv.setText(positionPreferences.getString(packageName+"y","")); 110 | delayEt.setText(delayPreferences.getString(packageName,"")); 111 | customLayout.setVisibility(View.VISIBLE); 112 | } 113 | else { 114 | removeAnchor(); 115 | customLayout.setVisibility(View.GONE); 116 | } 117 | } 118 | }); 119 | saveButton.setOnClickListener(new View.OnClickListener() { 120 | @Override 121 | public void onClick(View v) { 122 | String positionX = xDisTv.getText().toString(); 123 | String positionY= yDisTv.getText().toString(); 124 | Log.d(TAG, "onClick: "+positionX+" "+positionY); 125 | String delay = delayEt.getText().toString(); 126 | if (positionX.length()!=0 && positionY.length()!=0 && delay.length()!=0){ 127 | positionEditor.putString(packageName+"x",positionX); 128 | positionEditor.putString(packageName+"y",positionY); 129 | positionEditor.apply(); 130 | delayEditor.putString(packageName,delay); 131 | delayEditor.apply(); 132 | editor.putInt(packageName, ACTION_CUSTOM_CLICK); 133 | editor.apply(); 134 | customLayout.setVisibility(View.GONE); 135 | removeAnchor(); 136 | Toast.makeText(ActionSettingActivity.this,"保存成功",Toast.LENGTH_SHORT).show(); 137 | } 138 | else { 139 | Toast.makeText(ActionSettingActivity.this,"参数不能为空",Toast.LENGTH_SHORT).show(); 140 | } 141 | } 142 | }); 143 | switch (sharedPreferences.getInt(packageName, ACTION_VIEW_CLICK)) { 144 | case ACTION_NO_CLICK: 145 | actionRg.check(R.id.no_click_rb); 146 | break; 147 | case ACTION_SCREEN_CLICK: 148 | actionRg.check(R.id.screen_click_rb); 149 | break; 150 | case ACTION_CUSTOM_CLICK: 151 | actionRg.check(R.id.custom_click_rb); 152 | break; 153 | case ACTION_VIEW_CLICK: 154 | default: 155 | actionRg.check(R.id.view_click_rb); 156 | break; 157 | } 158 | } 159 | 160 | private void showAnchor() { 161 | anchorView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.view_anchor, null); 162 | windowManager = (WindowManager) getApplication().getSystemService(Context.WINDOW_SERVICE); 163 | WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(); 164 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 165 | layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 166 | } else { 167 | layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; 168 | } 169 | layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; 170 | layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; 171 | layoutParams.gravity = Gravity.CENTER; 172 | layoutParams.format = PixelFormat.TRANSPARENT; 173 | layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; 174 | windowManager.addView(anchorView, layoutParams); 175 | anchorView.setOnTouchListener(new View.OnTouchListener() { 176 | float originX,originY; 177 | float currentX,currentY; 178 | boolean isClick; 179 | @Override 180 | public boolean onTouch(View v, MotionEvent event) { 181 | Log.d(TAG, "onTouch: " + event.getX() + " " + event.getRawX()); 182 | switch (event.getAction()) { 183 | case MotionEvent.ACTION_DOWN: 184 | originX = currentX = event.getRawX(); 185 | originY = currentY = event.getRawY(); 186 | isClick = false; 187 | break; 188 | case MotionEvent.ACTION_MOVE: 189 | WindowManager.LayoutParams wmParams = (WindowManager.LayoutParams) anchorView.getLayoutParams(); 190 | wmParams.x += event.getRawX()-currentX; 191 | wmParams.y += event.getRawY()-currentY; 192 | windowManager.updateViewLayout(anchorView, wmParams); 193 | currentX = event.getRawX(); 194 | currentY = event.getRawY(); 195 | int[] location = new int[2]; 196 | anchorView.getLocationOnScreen(location); 197 | xDisTv.setText(String.valueOf(location[0]+anchorView.getWidth()/2)); 198 | yDisTv.setText(String.valueOf(location[1]+anchorView.getHeight()/2)); 199 | break; 200 | case MotionEvent.ACTION_UP: 201 | float translationX = Math.abs(event.getRawX()-originX); 202 | float translationY = Math.abs(event.getRawY()-originY); 203 | if (translationX<50 && translationY<50){ 204 | anchorView.performClick(); 205 | } 206 | break; 207 | default: 208 | break; 209 | } 210 | return false; 211 | } 212 | }); 213 | } 214 | 215 | private void removeAnchor(){ 216 | if (windowManager!=null && anchorView.isAttachedToWindow()){ 217 | windowManager.removeViewImmediate(anchorView); 218 | } 219 | } 220 | 221 | @Override 222 | protected void onDestroy() { 223 | super.onDestroy(); 224 | removeAnchor(); 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /app/src/main/java/com/ypp/adskip/AppListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.ypp.adskip; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.ImageView; 9 | import android.widget.TextView; 10 | 11 | import java.util.List; 12 | 13 | public class AppListAdapter extends BaseAdapter { 14 | private List appNodeInfoList; 15 | private Context mContext; 16 | public AppListAdapter(Context context, List appNodeInfoList) { 17 | this.mContext = context; 18 | this.appNodeInfoList = appNodeInfoList; 19 | } 20 | 21 | AppListAdapter(Context mContext) { 22 | this.mContext = mContext; 23 | } 24 | 25 | void setAppNodeInfoList(List appNodeInfoList) { 26 | this.appNodeInfoList = appNodeInfoList; 27 | notifyDataSetChanged(); 28 | } 29 | 30 | @Override 31 | public int getCount() { 32 | if (appNodeInfoList!=null){ 33 | return appNodeInfoList.size(); 34 | } 35 | return 0; 36 | } 37 | 38 | @Override 39 | public Object getItem(int position) { 40 | if (appNodeInfoList!=null && appNodeInfoList.size()>position){ 41 | return appNodeInfoList.get(position); 42 | } 43 | return null; 44 | } 45 | 46 | @Override 47 | public long getItemId(int position) { 48 | return 0; 49 | } 50 | 51 | @Override 52 | public View getView(int position, View convertView, ViewGroup parent) { 53 | ViewHolder viewHolder; 54 | if (convertView == null){ 55 | viewHolder = new ViewHolder(); 56 | convertView = LayoutInflater.from(mContext).inflate(R.layout.app_list_item, null); 57 | viewHolder.icon = convertView.findViewById(R.id.list_item_icon); 58 | viewHolder.name = convertView.findViewById(R.id.list_item_name); 59 | convertView.setTag(viewHolder); 60 | } 61 | else { 62 | viewHolder = (ViewHolder) convertView.getTag(); 63 | } 64 | 65 | AppNodeInfo appNodeInfo = appNodeInfoList.get(position); 66 | viewHolder.icon.setImageDrawable(appNodeInfo.getIcon()); 67 | viewHolder.name.setText(appNodeInfo.getName()); 68 | 69 | return convertView; 70 | } 71 | 72 | class ViewHolder{ 73 | ImageView icon; 74 | TextView name; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/java/com/ypp/adskip/AppNodeInfo.java: -------------------------------------------------------------------------------- 1 | package com.ypp.adskip; 2 | 3 | import android.graphics.drawable.Drawable; 4 | 5 | public class AppNodeInfo { 6 | private Drawable icon; 7 | private String packageName; 8 | private String name; 9 | 10 | AppNodeInfo(Drawable icon, String packageName, String name) { 11 | this.icon = icon; 12 | this.packageName = packageName; 13 | this.name = name; 14 | } 15 | 16 | Drawable getIcon() { 17 | return icon; 18 | } 19 | 20 | String getPackageName() { 21 | return packageName; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/ypp/adskip/AppsSettingActivity.java: -------------------------------------------------------------------------------- 1 | package com.ypp.adskip; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import androidx.core.widget.ContentLoadingProgressBar; 5 | 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.os.Handler; 9 | import android.text.Editable; 10 | import android.text.TextWatcher; 11 | import android.util.Log; 12 | import android.view.View; 13 | import android.widget.AdapterView; 14 | import android.widget.EditText; 15 | import android.widget.ListView; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | public class AppsSettingActivity extends AppCompatActivity { 21 | private final String TAG = "AppsSettingActivity"; 22 | private ListView appList; 23 | private EditText searchEt; 24 | private ContentLoadingProgressBar listLoadingPb; 25 | private AppListAdapter listAdapter; 26 | private List resultList; 27 | private List currentList; 28 | private Handler mHandler = new Handler(); 29 | @Override 30 | protected void onCreate(Bundle savedInstanceState) { 31 | super.onCreate(savedInstanceState); 32 | setContentView(R.layout.activity_apps_setting); 33 | setTitle(R.string.setting_activity_title); 34 | currentList = new ArrayList(); 35 | listAdapter = new AppListAdapter(AppsSettingActivity.this); 36 | appList = findViewById(R.id.app_list); 37 | searchEt = findViewById(R.id.search_et); 38 | listLoadingPb = findViewById(R.id.list_loading_pb); 39 | appList.setAdapter(listAdapter); 40 | appList.setOnItemClickListener(new AdapterView.OnItemClickListener() { 41 | @Override 42 | public void onItemClick(AdapterView parent, View view, int position, long id) { 43 | Intent intent = new Intent(AppsSettingActivity.this, ActionSettingActivity.class); 44 | intent.putExtra("packageName", currentList.get(position).getPackageName()); 45 | startActivity(intent); 46 | } 47 | }); 48 | new Thread(){ 49 | @Override 50 | public void run() { 51 | super.run(); 52 | final List appNodeInfoList = Utils.getInstalledAppList(getApplicationContext()); 53 | resultList = appNodeInfoList; 54 | currentList.addAll(appNodeInfoList); 55 | mHandler.post(new Runnable() { 56 | @Override 57 | public void run() { 58 | listAdapter.setAppNodeInfoList(appNodeInfoList); 59 | listLoadingPb.setVisibility(View.GONE); 60 | } 61 | }); 62 | } 63 | }.start(); 64 | searchEt.addTextChangedListener(new TextWatcher() { 65 | @Override 66 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 67 | 68 | } 69 | 70 | @Override 71 | public void onTextChanged(CharSequence s, int start, int before, int count) { 72 | filterApp(s.toString()); 73 | listAdapter.setAppNodeInfoList(currentList); 74 | } 75 | 76 | @Override 77 | public void afterTextChanged(Editable s) { 78 | 79 | } 80 | }); 81 | } 82 | 83 | private void filterApp(String key) { 84 | currentList.clear(); 85 | if (key==null||key.trim().length()==0){ 86 | currentList.addAll(resultList); 87 | return; 88 | } 89 | for (AppNodeInfo info : resultList){ 90 | if (info.getName().toLowerCase().contains(key.toLowerCase())){ 91 | currentList.add(info); 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /app/src/main/java/com/ypp/adskip/BootBroadcastReceiver.java: -------------------------------------------------------------------------------- 1 | package com.ypp.adskip; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.widget.Toast; 7 | 8 | public class BootBroadcastReceiver extends BroadcastReceiver { 9 | 10 | @Override 11 | public void onReceive(Context context, Intent intent) { 12 | switch (intent.getAction()){ 13 | case "android.intent.action.BOOT_COMPLETED": 14 | Intent serviceIntent = new Intent(context, AccessService.class); 15 | context.startService(serviceIntent); 16 | Toast.makeText(context, "ADSkip启动", Toast.LENGTH_SHORT).show(); 17 | default:break; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/ypp/adskip/ExecuteIntentService.java: -------------------------------------------------------------------------------- 1 | package com.ypp.adskip; 2 | 3 | import android.app.IntentService; 4 | import android.content.Intent; 5 | import android.content.Context; 6 | import android.content.SharedPreferences; 7 | import android.graphics.Rect; 8 | import android.os.Handler; 9 | import android.util.Log; 10 | import android.view.ViewConfiguration; 11 | import android.view.accessibility.AccessibilityNodeInfo; 12 | 13 | import java.sql.Time; 14 | import java.util.List; 15 | import java.util.Timer; 16 | 17 | public class ExecuteIntentService extends IntentService { 18 | private final String TAG = "ExecuteIntentService"; 19 | private SharedPreferences positionPreferences; 20 | private SharedPreferences delayPreferences; 21 | 22 | private static final String ACTION_EXECUTE = "com.ypp.adskip.action.EXECUTE"; 23 | 24 | private static final String EXTRA_INFO = "com.ypp.adskip.extra.INFO"; 25 | private static final String EXTRA_ACTION = "com.ypp.adskip.extra.ACTION"; 26 | private static final String EXTRA_PACKAGE = "com.ypp.adskip.extra.PACKAGE"; 27 | 28 | public ExecuteIntentService() { 29 | super("ExecuteIntentService"); 30 | } 31 | 32 | public static void startExecuteInfo(Context context, AccessibilityNodeInfo rootInfo, String packageName, int actionFlag) { 33 | Intent intent = new Intent(context, ExecuteIntentService.class); 34 | intent.setAction(ACTION_EXECUTE); 35 | intent.putExtra(EXTRA_INFO, rootInfo); 36 | intent.putExtra(EXTRA_ACTION, actionFlag); 37 | intent.putExtra(EXTRA_PACKAGE, packageName); 38 | context.startService(intent); 39 | } 40 | 41 | @Override 42 | protected void onHandleIntent(Intent intent) { 43 | if (positionPreferences==null || delayPreferences==null){ 44 | positionPreferences = getApplicationContext() 45 | .getSharedPreferences("app_position",MODE_PRIVATE); 46 | delayPreferences = getApplicationContext() 47 | .getSharedPreferences("app_delay",MODE_PRIVATE); 48 | } 49 | if (intent != null) { 50 | final String action = intent.getAction(); 51 | if (ACTION_EXECUTE.equals(action)) { 52 | final AccessibilityNodeInfo rootInfo = intent.getParcelableExtra(EXTRA_INFO); 53 | final int actionFlag = intent.getIntExtra(EXTRA_ACTION, Utils.ACTION_VIEW_CLICK); 54 | String packageName = intent.getStringExtra(EXTRA_PACKAGE); 55 | handleActionExecute(rootInfo, packageName, actionFlag); 56 | } 57 | } 58 | } 59 | 60 | 61 | private void handleActionExecute(AccessibilityNodeInfo rootInfo, String packageName, int actionFlag) { 62 | if (actionFlag==Utils.ACTION_CUSTOM_CLICK){ 63 | Log.d(TAG, "handleActionExecute: "+packageName); 64 | final int x = Integer.parseInt(positionPreferences.getString(packageName+"x","-1")); 65 | final int y = Integer.parseInt(positionPreferences.getString(packageName+"y","-1")); 66 | final int delay = Integer.parseInt(delayPreferences.getString(packageName,"1000")); 67 | Log.d(TAG, "handleActionExecute: "+x+" "+y+" "+delay); 68 | //AccessUtils.clickInScreen(x,y); 69 | new Thread(new Runnable() { 70 | @Override 71 | public void run() { 72 | try { 73 | Thread.sleep(delay); 74 | AccessUtils.clickInScreen(x,y); 75 | } catch (InterruptedException e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | }).start(); 80 | return; 81 | } 82 | List resultInfoList 83 | = rootInfo.findAccessibilityNodeInfosByText("跳过"); 84 | /*if (resultInfoList.isEmpty()){ 85 | resultInfoList = rootInfo.findAccessibilityNodeInfosByText("skip"); 86 | }*/ 87 | if (resultInfoList.isEmpty()) { 88 | resultInfoList = AccessUtils.findAccessibilityNodeInfosByIDContain(rootInfo, "skip"); 89 | } 90 | if (!resultInfoList.isEmpty()) { 91 | for (AccessibilityNodeInfo info : resultInfoList) { 92 | switch (actionFlag){ 93 | case Utils.ACTION_VIEW_CLICK: 94 | viewClick(info); 95 | Log.d(TAG, "handleActionExecute: view"); 96 | break; 97 | case Utils.ACTION_SCREEN_CLICK: 98 | screenClick(info); 99 | Log.d(TAG, "handleActionExecute: screen"); 100 | break; 101 | default: 102 | break; 103 | } 104 | info.recycle(); 105 | } 106 | stopSelf(); 107 | } 108 | } 109 | 110 | private void viewClick(AccessibilityNodeInfo info){ 111 | AccessUtils.click(info); 112 | } 113 | 114 | private void screenClick(AccessibilityNodeInfo info){ 115 | Rect rect = new Rect(); 116 | info.getBoundsInScreen(rect); 117 | AccessUtils.clickInScreen(rect.centerX(), rect.centerY()); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/com/ypp/adskip/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.ypp.adskip; 2 | 3 | import androidx.appcompat.app.AppCompatActivity; 4 | import androidx.appcompat.widget.AppCompatToggleButton; 5 | 6 | import android.content.Intent; 7 | import android.graphics.drawable.Drawable; 8 | import android.os.Bundle; 9 | import android.provider.Settings; 10 | import android.util.Log; 11 | import android.view.View; 12 | import android.widget.CompoundButton; 13 | import android.widget.LinearLayout; 14 | import android.widget.TextView; 15 | import android.widget.Toast; 16 | 17 | public class MainActivity extends AppCompatActivity { 18 | private final String TAG = "MainActivity"; 19 | private AppCompatToggleButton buttonStart; 20 | private TextView permissionTv; 21 | private LinearLayout permissionLayout; 22 | private LinearLayout appSettingLayout; 23 | private LinearLayout permissionOverlayLayout; 24 | private TextView permissionOverlayTv; 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_main); 29 | buttonStart = findViewById(R.id.button_start); 30 | buttonStart.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 31 | @Override 32 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 33 | Intent intent = new Intent(MainActivity.this, AccessService.class); 34 | if (isChecked){ 35 | startService(intent); 36 | } 37 | else { 38 | intent.putExtra("tryDisable",true); 39 | startService(intent); 40 | stopService(intent); 41 | } 42 | } 43 | }); 44 | permissionTv = findViewById(R.id.permission_tv); 45 | permissionLayout = findViewById(R.id.permission_layout); 46 | permissionLayout.setOnClickListener(new View.OnClickListener() { 47 | @Override 48 | public void onClick(View v) { 49 | if (AccessUtils.isAccessibilityServiceEnabled(getApplicationContext(), AccessService.class)){ 50 | Toast.makeText(MainActivity.this, "权限已开启",Toast.LENGTH_SHORT).show(); 51 | } 52 | else { 53 | startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)); 54 | } 55 | } 56 | }); 57 | permissionOverlayLayout = findViewById(R.id.permission_overlay_layout); 58 | permissionOverlayTv = findViewById(R.id.permission_overlay_tv); 59 | permissionOverlayLayout.setOnClickListener(new View.OnClickListener() { 60 | @Override 61 | public void onClick(View v) { 62 | if (Utils.canDrawOverlays(getApplicationContext())){ 63 | Toast.makeText(MainActivity.this, "权限已开启",Toast.LENGTH_SHORT).show(); 64 | } 65 | else { 66 | startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION)); 67 | } 68 | } 69 | }); 70 | appSettingLayout = findViewById(R.id.app_setting_layout); 71 | appSettingLayout.setOnClickListener(new View.OnClickListener() { 72 | @Override 73 | public void onClick(View v) { 74 | Intent intent = new Intent(MainActivity.this, AppsSettingActivity.class); 75 | startActivity(intent); 76 | } 77 | }); 78 | } 79 | 80 | @Override 81 | protected void onResume() { 82 | super.onResume(); 83 | Log.d(TAG, "onResume: "); 84 | permissionTv.post(new Runnable() { 85 | @Override 86 | public void run() { 87 | int height = permissionTv.getMeasuredHeight(); 88 | if (AccessUtils.isAccessibilityServiceEnabled(getApplicationContext(),AccessService.class)){ 89 | Drawable drawable = getResources().getDrawable(R.drawable.permission_yes, null); 90 | drawable.setBounds(0, (int) (0.1*height), (int) (0.8*height), (int) (0.9*height)); 91 | permissionTv.setCompoundDrawablesRelative(null,null,drawable,null); 92 | } 93 | else { 94 | Drawable drawable = getResources().getDrawable(R.drawable.permission_no, null); 95 | drawable.setBounds(0, (int) (0.1*height), (int) (0.8*height), (int) (0.9*height)); 96 | permissionTv.setCompoundDrawablesRelative(null,null,drawable,null); 97 | } 98 | } 99 | }); 100 | permissionOverlayTv.post(new Runnable() { 101 | @Override 102 | public void run() { 103 | int height = permissionOverlayTv.getMeasuredHeight(); 104 | if (Utils.canDrawOverlays(getApplicationContext())){ 105 | Drawable drawable = getResources().getDrawable(R.drawable.permission_yes, null); 106 | drawable.setBounds(0, (int) (0.1*height), (int) (0.8*height), (int) (0.9*height)); 107 | permissionOverlayTv.setCompoundDrawablesRelative(null,null,drawable,null); 108 | } 109 | else { 110 | Drawable drawable = getResources().getDrawable(R.drawable.permission_no, null); 111 | drawable.setBounds(0, (int) (0.1*height), (int) (0.8*height), (int) (0.9*height)); 112 | permissionOverlayTv.setCompoundDrawablesRelative(null,null,drawable,null); 113 | } 114 | } 115 | }); 116 | buttonStart.setChecked(Utils.isServiceRunning(getApplicationContext())); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/java/com/ypp/adskip/Utils.java: -------------------------------------------------------------------------------- 1 | package com.ypp.adskip; 2 | 3 | import android.Manifest; 4 | import android.app.AlertDialog; 5 | import android.content.Context; 6 | import android.content.SharedPreferences; 7 | import android.content.pm.ApplicationInfo; 8 | import android.content.pm.PackageInfo; 9 | import android.content.pm.PackageManager; 10 | import android.provider.Settings; 11 | import android.util.Log; 12 | import android.view.LayoutInflater; 13 | import android.view.View; 14 | import android.widget.ImageView; 15 | import android.widget.RadioGroup; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | 20 | class Utils { 21 | private static final String TAG = "Utils"; 22 | static final int ACTION_NO_CLICK = 1; 23 | static final int ACTION_VIEW_CLICK = 2; 24 | static final int ACTION_SCREEN_CLICK = 3; 25 | static final int ACTION_CUSTOM_CLICK = 4; 26 | private String[] permissions = {Manifest.permission.SYSTEM_ALERT_WINDOW}; 27 | static boolean isServiceRunning(Context context) { 28 | return getSharedPreferences(context).getBoolean("service",false); 29 | } 30 | 31 | static void setServiceRunning(Context context, boolean isRunning){ 32 | getSharedPreferences(context).edit().putBoolean("service", isRunning).apply(); 33 | } 34 | 35 | private static SharedPreferences getSharedPreferences(Context context){ 36 | return context.getSharedPreferences("config",Context.MODE_PRIVATE); 37 | } 38 | 39 | static List getInstalledAppList(Context context){ 40 | PackageManager packageManager = context.getPackageManager(); 41 | List appNodeInfoList = new ArrayList(); 42 | try { 43 | List applicationInfos = packageManager.getInstalledApplications(0); 44 | for (ApplicationInfo info : applicationInfos){ 45 | if (packageManager.getLaunchIntentForPackage(info.packageName) != null){ 46 | AppNodeInfo appNodeInfo = new AppNodeInfo( 47 | info.loadIcon(packageManager), 48 | info.packageName, 49 | (String) info.loadLabel(packageManager)); 50 | appNodeInfoList.add(appNodeInfo); 51 | } 52 | } 53 | } 54 | catch (Exception e){ 55 | e.printStackTrace(); 56 | } 57 | return appNodeInfoList; 58 | } 59 | 60 | static boolean canDrawOverlays(Context context){ 61 | return Settings.canDrawOverlays(context); 62 | } 63 | 64 | static void showChooseClickActionDialog(final Context context, final AppNodeInfo appNodeInfo){ 65 | View view = LayoutInflater.from(context).inflate(R.layout.dialog_choose, null); 66 | final AlertDialog.Builder builder = new AlertDialog.Builder(context); 67 | builder.setView(view); 68 | AlertDialog dialog =builder.create(); 69 | ImageView helpButton = view.findViewById(R.id.help_button); 70 | helpButton.setOnClickListener(new View.OnClickListener() { 71 | @Override 72 | public void onClick(View v) { 73 | AlertDialog.Builder hintBuilder = new AlertDialog.Builder(context); 74 | /*hintBuilder.setTitle("选项说明") 75 | .setItems(ACTION_DESCRIP,null);*/ 76 | View hintView = LayoutInflater.from(context).inflate(R.layout.dialog_descripton,null); 77 | hintBuilder.setView(hintView); 78 | hintBuilder.create().show(); 79 | } 80 | }); 81 | RadioGroup actionRg = view.findViewById(R.id.action_rg); 82 | final SharedPreferences sharedPreferences = context.getApplicationContext() 83 | .getSharedPreferences("app_action",Context.MODE_PRIVATE); 84 | switch (sharedPreferences.getInt(appNodeInfo.getPackageName(),ACTION_VIEW_CLICK)){ 85 | case ACTION_NO_CLICK: 86 | actionRg.check(R.id.no_click_rb); 87 | break; 88 | case ACTION_SCREEN_CLICK: 89 | actionRg.check(R.id.screen_click_rb); 90 | break; 91 | case ACTION_VIEW_CLICK: 92 | default: 93 | actionRg.check(R.id.view_click_rb); 94 | break; 95 | } 96 | actionRg.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { 97 | @Override 98 | public void onCheckedChanged(RadioGroup group, int checkedId) { 99 | Log.d(TAG, "onCheckedChanged: "+checkedId); 100 | SharedPreferences.Editor editor = sharedPreferences.edit(); 101 | switch (checkedId){ 102 | case R.id.no_click_rb: 103 | editor.putInt(appNodeInfo.getPackageName(),Utils.ACTION_NO_CLICK); 104 | break; 105 | case R.id.view_click_rb: 106 | editor.putInt(appNodeInfo.getPackageName(),Utils.ACTION_VIEW_CLICK); 107 | break; 108 | case R.id.screen_click_rb: 109 | editor.putInt(appNodeInfo.getPackageName(),Utils.ACTION_SCREEN_CLICK); 110 | break; 111 | default:break; 112 | } 113 | editor.apply(); 114 | } 115 | }); 116 | dialog.show(); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/anchor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Promisin/ADSkip/13e00bcc789c760d0e3fb8602dd0196436862ef7/app/src/main/res/drawable/anchor.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_circle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/dialog_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/help_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Promisin/ADSkip/13e00bcc789c760d0e3fb8602dd0196436862ef7/app/src/main/res/drawable/help_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Promisin/ADSkip/13e00bcc789c760d0e3fb8602dd0196436862ef7/app/src/main/res/drawable/icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/notify_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Promisin/ADSkip/13e00bcc789c760d0e3fb8602dd0196436862ef7/app/src/main/res/drawable/notify_icon.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/permission_no.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Promisin/ADSkip/13e00bcc789c760d0e3fb8602dd0196436862ef7/app/src/main/res/drawable/permission_no.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/permission_yes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Promisin/ADSkip/13e00bcc789c760d0e3fb8602dd0196436862ef7/app/src/main/res/drawable/permission_yes.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/search_box_edittext_keyword_bg.9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Promisin/ADSkip/13e00bcc789c760d0e3fb8602dd0196436862ef7/app/src/main/res/drawable/search_box_edittext_keyword_bg.9.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/timg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Promisin/ADSkip/13e00bcc789c760d0e3fb8602dd0196436862ef7/app/src/main/res/drawable/timg.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/toggle_button_active.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toggle_button_active_press.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toggle_button_inactive.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/toggle_button_inactive_press.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_action_setting.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 26 | 27 | 35 | 36 | 44 | 45 | 53 | 54 | 55 | 63 | 64 | 74 | 84 | 94 | 104 | 114 | 115 | 126 | 135 | 146 | 147 |