安卓12机顶盒 静默更新app

公司最近需要在机顶盒上面是实现更新app,但是要做无感更新,让用户无法感知

刚开始想的是使用热更新,但是发现好像实现不了版本更新,最后在博客上发现,都是用无障碍,或者adb得比较多,做一下总结

第一种使用安卓无障碍模式进行自动点击

   使用无障碍模式,我用得是两个app,用一个app来执行无障碍,更新本体app进行下载,使用的是

AccessibilityService服务

代码:

public class MyAccessibilityService extends AccessibilityService  {

    Map<Integer, Boolean> handleMap = new HashMap<>();

    private static final String TAG = MyAccessibilityService.class.getSimpleName();
    private static final int DELAY_PAGE = 320; // 页面切换时间
    private final Handler mHandler = new Handler();

    @Override
    public void onAccessibilityEvent(AccessibilityEvent event) {
        log(event.toString());
        checkInstall(event);

    }

    private void log(String string) {
        Log.e("getClass()", "AutoInstallAccessibilityService:"
                + string);

    }

    /**
     * 检查是否是要安装应用的标题
     *
     * @param source
     * @param title
     * @return
     */
    private boolean checkTitle(AccessibilityNodeInfo source, String title) {
        List<AccessibilityNodeInfo> titleList = source
                .findAccessibilityNodeInfosByText(title);
        for (AccessibilityNodeInfo node : titleList) {
            Log.d("test", node.toString());
            if (node.getClassName().equals("android.widget.TextView")) {
                return true;
            }
        }
        return false;
    }

    /**
     * 判断是否需要安装应用,并执行
     * @param event
     */
    private void checkInstall(AccessibilityEvent event) {
        // source不为空
        boolean notNull = event.getSource() != null;
        if (notNull) {

            // 当前界面为安装界面
            boolean installPage = event.getPackageName().equals(
                    "com.android.packageinstaller");
            Log.e(TAG, "installPage:--- "+installPage );
            if (installPage) {
                // 当前标题为应用标题
                boolean thisApk = checkTitle(event.getSource(), "木鹰互动");
                Log.e(TAG, "thisApk:--- "+thisApk );
                if (thisApk) {
                    doInstall(event);
                }
            }
        }

    }


    /**
     * 执行 安装 并打开应用
     *
     * @param event
     */
    private void doInstall(AccessibilityEvent event) {
        List<AccessibilityNodeInfo> unintall_nodes = event.getSource()
                .findAccessibilityNodeInfosByText("更新");
        for (int i = 0; i < unintall_nodes.size(); i++) {
            Log.e(TAG, "doInstall: "+unintall_nodes.get(i).getClassName() );
        }
        if (unintall_nodes != null && !unintall_nodes.isEmpty()) {
            AccessibilityNodeInfo node;
            for (int i = 0; i < unintall_nodes.size(); i++) {
                node = unintall_nodes.get(i);
                if (node.getClassName().equals("android.widget.Button")
                        && node.isEnabled()) {
                    node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                }
            }
        }
        List<AccessibilityNodeInfo> unintall_nodes1 = event.getSource()
                .findAccessibilityNodeInfosByText("安装");
        if (unintall_nodes1 != null && !unintall_nodes1.isEmpty()) {
            AccessibilityNodeInfo node;
            for (int i = 0; i < unintall_nodes1.size(); i++) {
                node = unintall_nodes1.get(i);
                if (node.getClassName().equals("android.widget.Button")
                        && node.isEnabled()) {
                    node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                }
            }
        }
        List<AccessibilityNodeInfo> next_nodes = event.getSource()
                .findAccessibilityNodeInfosByText("下一步");
        if (next_nodes != null && !next_nodes.isEmpty()) {
            AccessibilityNodeInfo node;
            for (int i = 0; i < next_nodes.size(); i++) {
                node = next_nodes.get(i);
                if (node.getClassName().equals("android.widget.Button")
                        && node.isEnabled()) {
                    node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                }
            }
        }

        List<AccessibilityNodeInfo> ok_nodes = event.getSource()
                .findAccessibilityNodeInfosByText("打开");
        if (ok_nodes != null && !ok_nodes.isEmpty()) {
            AccessibilityNodeInfo node;
            for (int i = 0; i < ok_nodes.size(); i++) {
                node = ok_nodes.get(i);
                if (node.getClassName().equals("android.widget.Button")
                        && node.isEnabled()) {
                    node.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                }
            }
        }
    }

    @Override
    public void onInterrupt() {
        // TODO Auto-generated method stub

    }

}

activity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        boolean screenReaderActive = AccessibilityUtil.isScreenReaderActive(MainActivity.this);
        if (!screenReaderActive) {
            Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
            startActivity(intent);
        }


        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.System.canWrite(MainActivity.this)) {
                Intent intent = new Intent(Settings.ACTION_MANAGE_WRITE_SETTINGS,
                        Uri.parse("package:" + getPackageName()));
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivityForResult(intent, 200);
            } else {
                // 如果有权限做些什么
               // Toast.makeText(this, "已申请", Toast.LENGTH_SHORT).show();

            }
        }
    }

    @Override
    protected void onResume() {
        boolean screenReaderActive = AccessibilityUtil.isScreenReaderActive(MainActivity.this);
        if (screenReaderActive) {
            Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
            startActivity(intent);
        }
        super.onResume();
    }

    //自动开启无障碍服务
    private void autoOpenAccessibilityService(){
        Settings.Secure.putString(
                getContentResolver(),
                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                "包名/想要打开的无障碍服务的完整路径"
        );
        //例子:"com.xxx.xxxx/com.xxx.xxxx.service.MyAccessibilityService"
        Settings.Secure.putString(
                getContentResolver(),
                Settings.Secure.ACCESSIBILITY_ENABLED, "1"
        );
    }
    //自动关闭无障碍服务
    private void autoCloseAccessibilityService(){
        Settings.Secure.putString(
                getContentResolver(),
                Settings.Secure.ACCESSIBILITY_ENABLED,
                "包名/想要打开的无障碍服务完整路径"
        );
    //例子:"com.xxx.xxxx/com.xxx.xxxx.service.MyAccessibilityService"
        Settings.Secure.putString(
                getContentResolver(),
                Settings.Secure.ACCESSIBILITY_ENABLED, "0"
        );
    }

}

工具类

public class AccessibilityUtil {
    /**
     * 检查系统设置:是否开启辅助服务
     * @param service 辅助服务
     */
    private static boolean isSettingOpen(Class service, Context cxt) {
        try {
            int enable = Settings.Secure.getInt(cxt.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0);
            if (enable != 1)
                return false;
            String services = Settings.Secure.getString(cxt.getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
            if (!TextUtils.isEmpty(services)) {
                TextUtils.SimpleStringSplitter split = new TextUtils.SimpleStringSplitter(':');
                split.setString(services);
                while (split.hasNext()) { // 遍历所有已开启的辅助服务名
                    if (split.next().equalsIgnoreCase(cxt.getPackageName() + "/" + service.getName()))
                        return true;
                }
            }
        } catch (Throwable e) {//若出现异常,则说明该手机设置被厂商篡改了,需要适配
            Log.e("TAG", "isSettingOpen: " + e.getMessage());
        }
        return false;
    }

    /**
     * 跳转到系统设置:开启辅助服务
     */
    public static void jumpToSetting(final Context cxt) {
        try {
            cxt.startActivity(new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS));
        } catch (Throwable e) {//若出现异常,则说明该手机设置被厂商篡改了,需要适配
            try {
                Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                cxt.startActivity(intent);
            } catch (Throwable e2) {
                Log.e("TAG", "jumpToSetting: " + e2.getMessage());
            }
        }
    }



    public static boolean isAccessibilityEnabled(Context context) throws RuntimeException{
        if (context == null) {
            return false;
        }

        // 检查AccessibilityService是否开启
        AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
        boolean isAccessibilityEnabled_flag = am.isEnabled();

        boolean isExploreByTouchEnabled_flag = false;

        // 检查无障碍服务是否以语音播报的方式开启
        isExploreByTouchEnabled_flag = isScreenReaderActive(context);

        return (isAccessibilityEnabled_flag && isExploreByTouchEnabled_flag);

    }
    private final static String SCREEN_READER_INTENT_ACTION = "android.accessibilityservice.AccessibilityService";
    private final static String SCREEN_READER_INTENT_CATEGORY = "android.accessibilityservice.category.FEEDBACK_SPOKEN";

    public static boolean isScreenReaderActive(Context context) {

        // 通过Intent方式判断是否存在以语音播报方式提供服务的Service,还需要判断开启状态
        Intent screenReaderIntent = new Intent(SCREEN_READER_INTENT_ACTION);
        screenReaderIntent.addCategory(SCREEN_READER_INTENT_CATEGORY);
        List<ResolveInfo> screenReaders = context.getPackageManager().queryIntentServices(screenReaderIntent, 0);
        // 如果没有,返回false
        if (screenReaders == null || screenReaders.size() <= 0) {
            return false;
        }

        boolean hasActiveScreenReader = false;
        if (Build.VERSION.SDK_INT <= 15) {
            ContentResolver cr = context.getContentResolver();
            Cursor cursor = null;
            int status = 0;

            for (ResolveInfo screenReader : screenReaders) {
                cursor = cr.query(Uri.parse("content://" + screenReader.serviceInfo.packageName
                        + ".providers.StatusProvider"), null, null, null, null);

                if (cursor != null && cursor.moveToFirst()) {
                    status = cursor.getInt(0);
                    cursor.close();
                    // 状态1为开启状态,直接返回true即可
                    if (status == 1) {
                        return true;
                    }
                }
            }
        } else if (Build.VERSION.SDK_INT >= 26) {
            // 高版本可以直接判断服务是否处于开启状态
            for (ResolveInfo screenReader : screenReaders) {
                hasActiveScreenReader |= isAccessibilitySettingsOn(context, screenReader.serviceInfo.packageName + "/" + screenReader.serviceInfo.name);
            }

        } else {
            // 判断正在运行的Service里有没有上述存在的Service
            List<String> runningServices = new ArrayList<String>();

            android.app.ActivityManager manager = (android.app.ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            for (android.app.ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
                runningServices.add(service.service.getPackageName());
            }

            for (ResolveInfo screenReader : screenReaders) {
                if (runningServices.contains(screenReader.serviceInfo.packageName)) {
                    hasActiveScreenReader |= true;
                }
            }
        }

        return hasActiveScreenReader;
    }

    // To check if service is enabled
    private static boolean isAccessibilitySettingsOn(Context context, String service) {

        TextUtils.SimpleStringSplitter mStringColonSplitter = new TextUtils.SimpleStringSplitter(':');
        String settingValue = Settings.Secure.getString(
                context.getApplicationContext().getContentResolver(),
                Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
        if (settingValue != null) {
            mStringColonSplitter.setString(settingValue);
            while (mStringColonSplitter.hasNext()) {
                String accessibilityService = mStringColonSplitter.next();
                if (accessibilityService.equalsIgnoreCase(service)) {
                    return true;
                }
            }
        }

        return false;
    }

}

清单文件

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:sharedUserId="android.uid.system"
    >
    <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS"
        tools:ignore="ProtectedPermissions" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>


    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApplication"
        tools:targetApi="31">
        <service android:name=".ui.MyAccessibilityService"
            android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
            android:exported="true">
            <intent-filter>
                <action android:name="android.accessibilityservice.AccessibilityService"/>
            </intent-filter>

            <meta-data
                android:name="android.accessibilityservice"
                android:resource="@xml/accessibility_service_config"/>
        </service>

        <activity
            android:name=".ui.MainActivity"
            android:exported="true"
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

accessibility_service_config.xml

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFeedbackType="feedbackGeneric"
    android:accessibilityFlags="flagDefault"
    android:canRetrieveWindowContent="true"
    android:description="@string/app_title"
    android:notificationTimeout="100"
    android:packageNames="com.android.packageinstaller" />

        

        这个使用来监控系统安装的弹窗,检测到之后,会自动点击,完成安装,缺点就是当这个app挂掉之后,无法实现自动安装,无障碍还需要手动开启,调研之后发现,如果要实现无障碍一直开启的话,还需把app打包成系统级别应用(没有实现)

参考博客自动和手动开启无障碍服务的方式_settings.secure.putstring-CSDN博客

代码开启无障碍服务-系统应用_settings.secure.putstring(getcontentresolver(), se-CSDN博客

无障碍系统自启动_车机无障碍权限命令-CSDN博客

安卓默认打开指定apk的无障碍权限_android 12.0默认开启无障碍服务权限和打开默认apk无障碍服务-CSDN博客

第二种 使用adb 命令安装apk(root)

        root 权限还在申请,实现之后更新

  • 8
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值