【安卓】开发跳过广告app,具备“李跳跳”app的部分功能

news2024/11/26 0:58:06

前言

现在手机的开屏广告还是挺多的,还有应用内弹出广告,青少年模式等,市面上很多跳过广告app下架了,我利用工作闲暇时间开发了自己用的app,不传播,分享知识!

实现思路

利用手机的无障碍服务,该服务可以监听屏幕窗口的layout树,通过遍历窗口的View,检测到包含“跳过”、“关闭”等按钮,执行自动化点击事件,大部分跳过广告的原理都是这样的,根据弹窗规则匹配的JSON文件里面定义的上百种APP的弹窗规则,可以跳过大部分弹窗。

应用截图

平板截图

代码结构

在这里插入图片描述
包括两个读取规则实体类,一个无障碍服务类,一个Activity类和一个规则JSON文件。

主要代码

一、activity类

package com.lsl.adskip;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import android.Manifest;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;

import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.ImageButton;
import android.widget.Switch;
import android.widget.TextView;
import android.widget.Toast;

import com.lsl.adskip.service.MyService;

import java.time.LocalDateTime;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    private ImageButton imageBtn;
    private TextView tipView;
    private Switch switchButton;
    private Switch dialogSwitch;
    private Switch superModeBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
    }

    private void init() {
        imageBtn = findViewById(R.id.imageBtn);
        tipView = findViewById(R.id.tip);
        imageBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
                startActivity(intent);
            }
        });
        switchButton = findViewById(R.id.switch_button);
        SharedPreferences sharedPreferences = getSharedPreferences("serviceState", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        switchButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                // 状态改变时的逻辑处理
                if (isChecked) {
                    // 开启屏蔽广告
                    if (MyService.isServiceEnable()) {
                        MyService.serviceState = true;
                        switchButton.setChecked(true);
                        editor.putBoolean("serviceState", true);
                    } else {
                        switchButton.setChecked(false);
                        showDialog();
                    }
                } else {
                    // 开关关闭时的操作
                    MyService.serviceState = false;
                    switchButton.setChecked(false);
                    editor.putBoolean("serviceState", false);
                }
                editor.apply();
            }
        });
        dialogSwitch = findViewById(R.id.switch_dialog);
        SharedPreferences dialogState = getSharedPreferences("dialogAD", Context.MODE_PRIVATE);
        SharedPreferences.Editor adEditor = dialogState.edit();
        dialogSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    if (MyService.isServiceEnable()) {
                        adEditor.putBoolean("dialogAD", true);
                        MyService.dialogAD = true;
                        dialogSwitch.setChecked(true);
                    } else {
                        dialogSwitch.setChecked(false);
                        showDialog();
                    }
                } else {
                    adEditor.putBoolean("dialogAD", false);
                    MyService.dialogAD = false;
                    dialogSwitch.setChecked(false);
                }
                adEditor.apply();
            }
        });

        superModeBtn = findViewById(R.id.superMode);
        SharedPreferences superSwitch = getSharedPreferences("superSwitch",Context.MODE_PRIVATE);
        SharedPreferences.Editor superEditor = superSwitch.edit();
        superModeBtn.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked){
                    if (MyService.isServiceEnable()){
                        MyService.superMode = true;
                        superEditor.putBoolean("superSwitch",true);
                        showSuperTip();
                    }else{
                        superModeBtn.setChecked(false);
                        showDialog();
                    }
                }else{
                    superEditor.putBoolean("superSwitch",false);
                    MyService.superMode = false;
                }
                superEditor.apply();
            }
        });
    }

    @Override
    public void onPause(){
        Log.d("应用","暂停");
        super.onPause();
    }
    @Override
    public void onResume() {
        if (MyService.isServiceEnable()) {
            tipView.setText("无障碍服务已启动");
            imageBtn.setImageResource(R.drawable.laugh);
            SharedPreferences sharedPreferences = getSharedPreferences("serviceState", Context.MODE_PRIVATE);
            boolean state = sharedPreferences.getBoolean("serviceState", false);
            MyService.serviceState = state;
            boolean dialogState = getSharedPreferences("dialogAD", Context.MODE_PRIVATE).getBoolean("dialogAD", false);
            MyService.dialogAD = dialogState;
            dialogSwitch.setChecked(dialogState);
            switchButton.setChecked(state);
            boolean superModeState = getSharedPreferences("superSwitch",Context.MODE_PRIVATE).getBoolean("superSwitch",false);
            superModeBtn.setChecked(superModeState);
            MyService.superMode = superModeState;
        } else {
            tipView.setText(R.string.tip);
            imageBtn.setImageResource(R.drawable.cray);
            dialogSwitch.setChecked(false);
            switchButton.setChecked(false);
            MyService.dialogAD = false;
            MyService.serviceState = false;
            showDialog();
        }
        super.onResume();
    }

    private void showDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle("服务状态");
        builder.setMessage("无障碍服务未开启,无法使用,请进入系统无障碍服务中找到本APP,开启服务。温馨提示:本应用没有网络功能,也无需授权手机隐私相关权限,您的隐私是百分百安全的,请放心使用!");
        builder.setPositiveButton("去设置", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
                startActivity(intent);
            }
        });
        builder.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                Toast toast = Toast.makeText(MainActivity.this, "您已取消,如需正常使用,请点击熊猫头", Toast.LENGTH_SHORT);
                toast.show();
            }
        });
        AlertDialog dialog = builder.create();
        dialog.show();
    }

    private void showSuperTip(){
        AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
        builder.setTitle("服务说明");
        builder.setMessage("你已开启超级模式,该模式收录了几百种APP的弹窗匹配规则,原理是监听弹窗类型,并自动帮你点击对应按钮,如你在使用其他App过程中出现了意料之外的效果,请关闭该模式");
        builder.setNegativeButton("我已知晓", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
            }
        });
        AlertDialog dialog = builder.create();
        dialog.show();
    }

    @Override
    public void onDestroy(){
        super.onDestroy();
        Log.d("应用","重启");
        //startAllApp();
    }
}

2、MyService类

package com.lsl.adskip.service;

import android.accessibilityservice.AccessibilityService;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.Toast;


import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

import com.lsl.adskip.MainActivity;
import com.lsl.adskip.R;
import com.lsl.adskip.entity.RuleDetail;
import com.lsl.adskip.entity.RuleEntity;
import com.lsl.adskip.util.ToastUtil;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyService extends AccessibilityService {
    private static MyService instance;
    public static boolean serviceState = false;
    public static boolean dialogAD = false;
    public static boolean superMode = false;
    private ExecutorService executor = Executors.newFixedThreadPool(4);
    private List<RuleEntity> reluList;

    public static boolean isServiceEnable() {
        return instance != null;
    }

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        if (serviceState) {
            if (event != null) {
                //跳过广告逻辑
                Log.d("监听中", "开屏广告");
                AccessibilityNodeInfo currentNodeInfo = getCurrentRootNode();
                if (currentNodeInfo != null) {
                    List<AccessibilityNodeInfo> skipBtnNodes = currentNodeInfo.findAccessibilityNodeInfosByText("跳过");
                    List<AccessibilityNodeInfo> closeBtnNodes = currentNodeInfo.findAccessibilityNodeInfosByText("关闭");
                    if (closeBtnNodes != null && !closeBtnNodes.isEmpty()) {
                        AccessibilityNodeInfo ad = closeBtnNodes.get(0);
                        Log.d("检测到广告节点:", String.valueOf(ad));
                        ad.performAction(AccessibilityNodeInfo.ACTION_CLICK);

                    }
                    if (skipBtnNodes != null && !skipBtnNodes.isEmpty()) {
                        AccessibilityNodeInfo ac = skipBtnNodes.get(0);
                        Log.d("检测到广告节点:", String.valueOf(ac));
                        ac.performAction(AccessibilityNodeInfo.ACTION_CLICK);

                    }
                }
            }
        }
        //监听弹窗广告
        if (dialogAD) {
            if (event != null) {
                Log.d("监听中", "弹窗广告");
                AccessibilityNodeInfo dialogNodeInfo = getCurrentRootNode();
                if (dialogNodeInfo != null) {
                    String viewId = dialogNodeInfo.getPackageName() + ":id/" + "close";
                    List<AccessibilityNodeInfo> closeNodes = dialogNodeInfo.findAccessibilityNodeInfosByViewId(viewId);
                    if (closeNodes != null && !closeNodes.isEmpty()) {
                        AccessibilityNodeInfo ac = closeNodes.get(0);
                        ac.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                    }
                }
            }
        }
        if (event != null && superMode) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    if (reluList != null) {
                        for (RuleEntity re : reluList) {
                            for (RuleDetail rd : re.getRules()) {
                                if (searchNode(rd.getId()) != null) {
                                    AccessibilityNodeInfo node = searchNode(rd.getAction());
                                    if (node != null) {
                                        node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                                        Log.d("模式", "超级模式:" + String.valueOf(node));
                                    }
                                }
                            }
                        }
                    }
                }
            });
        }
    }

    @Override
    public void onInterrupt() {
        Log.d("服务状态", "断开连接");
    }

    @Override
    public void onServiceConnected() {
        super.onServiceConnected();
        instance = this;
        Log.d("服务状态", "连接成功");
        ToastUtil.showLongToast(this, "服务已连接");
        //startForeNotification();
        executor.execute(new Runnable() {
            @Override
            public void run() {
                reluList = readJsonToRuleList();
                Log.d("加载", "自定义规则已加载");
            }
        });
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("服务状态", "服务重启");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        Log.d("生命周期", "destroy");
        ToastUtil.showLongToast(this, "屏蔽广告服务已被销毁");
        instance = null;
        executor.shutdown();
    }

    private AccessibilityNodeInfo getCurrentRootNode() {
        try {
            return getRootInActiveWindow();
        } catch (Exception e) {
            if (e.getMessage() != null) {
                Log.e("根节点异常", e.getMessage());
            }
            return null;
        }
    }


    /**
     * 读取自定义广告类型JSON文件生成规则实体列表
     */
    public List<RuleEntity> readJsonToRuleList() {
        List<RuleEntity> ruleEntityList = new ArrayList<>();
        try {
            InputStream inputStream = getResources().openRawResource(R.raw.all_rules);
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            StringBuilder sb = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                sb.append(line);
            }
            reader.close();
            inputStream.close();
            JSONArray jsonArray = new JSONArray(sb.toString());
            for (int i = 0; i < jsonArray.length(); i++) {
                JSONObject jsonObject = jsonArray.getJSONObject(i);
                Iterator<String> keys = jsonObject.keys();
                while (keys.hasNext()) {
                    String key = keys.next();
                    String value = jsonObject.getString(key);
                    JSONObject ruleEntityJson = new JSONObject(value);
                    JSONArray popupRules = ruleEntityJson.getJSONArray("popup_rules");
                    RuleEntity ruleEntity = new RuleEntity();
                    ArrayList<RuleDetail> ruleDetails = new ArrayList<>();
                    for (int j = 0; j < popupRules.length(); j++) {
                        JSONObject ruleObject = popupRules.getJSONObject(j);
                        RuleDetail ruleDetail = new RuleDetail(ruleObject.getString("id"),
                                ruleObject.getString("action"));
                        ruleDetails.add(ruleDetail);
                    }
                    ruleEntity.setRules(ruleDetails);
                    ruleEntityList.add(ruleEntity);
                }
            }
            return ruleEntityList;
        } catch (IOException | JSONException e) {
            e.printStackTrace();
        }
        return ruleEntityList;
    }

    /**
     * 匹配广告节点
     * @param filter
     * @return
     */
    private AccessibilityNodeInfo searchNode(String filter) {
        AccessibilityNodeInfo rootNode = getCurrentRootNode();
        if (rootNode != null) {
            List<AccessibilityNodeInfo> nodeInfosByText = rootNode.findAccessibilityNodeInfosByText(filter);
            if (!nodeInfosByText.isEmpty()) {
                return nodeInfosByText.get(0);
            }
            String viewId = rootNode.getPackageName() + ":id/" + filter;
            List<AccessibilityNodeInfo> nodeInfosByViewId = rootNode.findAccessibilityNodeInfosByViewId(viewId);
            if (!nodeInfosByViewId.isEmpty()) {
                return nodeInfosByViewId.get(0);
            }
        }
        return null;
    }
}

3、完整代码

完整代码已经上传到gitee里面了,可以移步下载,源码链接:广告滚犊子
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1042859.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ARM day1

1.复习今日内容 2.搭建汇编环境 下发资料-》工具软件 -》汇编环境搭建 3.安装Ubuntu下的交叉编译工具链 思维导图&#xff1a;

MQ - 31 基础功能: 优先级队列的设计

文章目录 导图概述什么是优先级队列如何设计实现优先级队列业务实现优先级队列的效果内核支持优先级队列RabbitMQ 中优先级队列的实现总结导图 概述 当我们需要在业务中对消息设置优先级,让优先级高的消息能被优先消费,此时就需要用到消息队列中优先级队列的特性。 为了了解…

玩转Mysql系列 - 第23篇:mysql索引管理详解

这是Mysql系列第23篇。 环境&#xff1a;mysql5.7.25&#xff0c;cmd命令中进行演示。 代码中被[]包含的表示可选&#xff0c;|符号分开的表示可选其一。 关于索引的&#xff0c;可以先看一下前2篇文章&#xff1a; 什么是索引&#xff1f; mysql索引原理详解 本文主要介…

Sui资助申请指南,310万美元资助金已成功申领

Sui基金会致力于资助开发者、构建者、教育工作者、研究人员以及其他推动和推广Sui生态发展的社区成员。立即申请&#xff1a;https://airtable.com/shrkLWBRNPL89f0SX 资助计划类型 构建者资助计划 通过推动Sui的全球采用&#xff0c;帮助引导下一个十亿用户进入Web3的项目。…

外包干了3个月,技术退步明显。。。。。

先说一下自己的情况&#xff0c;大专生&#xff0c;17年通过校招进入广州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落!而我已经在一个企业干了四年的功能测试…

使用 Microsoft 365 进行移动设备管理

什么是 Microsoft 365 的移动设备管理 移动设备通过允许员工访问文件、电子邮件、联系人等&#xff0c;在帮助员工随时随地工作方面发挥了巨大作用&#xff0c;允许员工将公司拥有的移动设备用于工作目的可能意味着允许员工在不在办公场所时从外部网络访问资源&#xff0c;这使…

人工智能在医疗中的应用:医院陪诊系统的前沿技术

人工智能在医疗领域的应用已经带来了巨大的变革&#xff0c;其中之一是医院陪诊系统。这些系统利用机器学习和自然语言处理等人工智能技术&#xff0c;改善了患者体验&#xff0c;提高了医疗机构的效率。本文将讨论医院陪诊系统的前沿技术&#xff0c;并提供一个简单的示例代码…

密码学【第三节:对称密码-DES\AES】

前言 在密码学中&#xff0c;加密算法分为双向加密和单向加密。单向加密包括MD5、SHA等摘要算法&#xff0c;它们是不可逆的。双向加密包括对称加密和非对称加密&#xff0c;对称加密包括AES加密、DES加密等。双向加密是可逆的&#xff0c;存在密文的密钥。AES算法是DES算法的替…

Java 大厂八股文面试专题-JVM相关面试题 类加载器

Java 大厂八股文面试专题-设计模式 工厂方法模式、策略模式、责任链模式-CSDN博客 JVM相关面试题 1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序…

openGauss学习笔记-81 openGauss 数据库管理-内存优化表MOT管理-内存表特性-MOT使用概述

文章目录 openGauss学习笔记-81 openGauss 数据库管理-内存优化表MOT管理-内存表特性-MOT使用概述 openGauss学习笔记-81 openGauss 数据库管理-内存优化表MOT管理-内存表特性-MOT使用概述 MOT作为openGauss的一部分自动部署。有关如何计算和规划所需的内存和存储资源以维持工…

Vue watch实时计算器

watch实时计算器 可以自己选择、-、*、 参考代码 <!DOCTYPE html> <html> <head><meta charset"utf-8"><title></title><script src"https://cdn.bootcdn.net/ajax/libs/vue/2.7.10/vue.js"></script>…

Redis安装部署和常用命令

文章目录 一、关系数据库与非关系型数据库1.1 关系型数据库1.2 非关系型数据库1.3 关系型数据库和非关系型数据库区别1.4 非关系型数据库产生背景1.5 小结 二、Redis基本概念2.1 Redis 简介2.2 Redis的优点2.3 使用场景2.4 哪些数据适合放入缓存中&#xff1f;2.5 Redis的数据类…

2015-2022年商道融绿 ESG数据

2015-2022年商道融绿 ESG数据 1、时间&#xff1a;2015-2022年 2、指标&#xff1a;证券代码、证券简称、商道融绿ESG评级 3、范围&#xff1a;沪深A股上市公司 4、来源&#xff1a;整理自wind 5、指标解释&#xff1a; 商道融绿&#xff08;Sustainalytics&#xff09;E…

【Flink、java】

依赖 <dependency><groupId>org.apache.flink</groupId><artifactId>flink-streaming-java_2.11</artifactId><version>1.14.6</version></dependency><dependency><groupId>org.apache.flink</groupId>&…

jenkins流水线实现xjar加固

jenkins流水线实现xjar加固 xjar的定义 Spring Boot JAR 安全加密运行工具&#xff0c;同时支持的原生JAR。 基于对JAR包内资源的加密以及拓展ClassLoader来构建的一套程序加密启动&#xff0c;动态解密运行的方案&#xff0c;避免源码泄露或反编译功能特性 无需侵入代码&am…

nvm nodejs的版本管理工具

nvm 全英文名叫 node.js version management&#xff0c;是一个 nodejs 的版本管理工具&#xff0c;为了解决 nodejs 各种版本存在不兼容现象可以通过他安装和切换不同版本的 nodejs。 一、完全删除之前的 node 和 npm 1. 打开 cmd 命令窗口&#xff0c;输入 npm cache clean…

rs渲染VRAM内存不足怎么解决?Redshift 处理 VRAM 不足的情况

在某些极少数情况下&#xff0c;Redshift 可能会生成以下类型的消息并中止渲染&#xff1a; Redshift cannot operate with less than 256MB of free VRAM. Frame rendering aborted. 或者… There is less than 128MB of free VRAM once fixed data and minimal ray memory ar…

用简单例子讲清楚webgl模板测试

文章目录 搭建简易的webgl环境绘制简单三角形&#xff08;不带stencilTest)绘制另一个三角形&#xff08;不带模板测试&#xff09;加入模板测试总结调参练习 搭建简易的webgl环境 一直以来&#xff0c;我只是想通过搭建纯webgl环境&#xff0c;进行开发&#xff0c;来清楚地了…

计算机毕业设计 基于微信小程序的学习资料销售平台的设计与实现 Java实战项目 附源码+文档+视频讲解

博主介绍&#xff1a;✌从事软件开发10年之余&#xff0c;专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精…