zoukankan      html  css  js  c++  java
  • 多种方式实现动态替换Android默认桌面Launcher

    多种方式实现动态替换Android默认桌面Launcher
    文章目录
    多种方式实现动态替换Android默认桌面Launcher
    背景简介
    技术方案
    三种方案
    方案一
    方案二
    方案三
    风险
    背景简介
    Launcher-是安卓系统中的桌面启动器,安卓系统的桌面UI统称为Launcher。Launcher是安卓系统中的主要程序组件之一,安卓系统中如果没有Launcher就无法启动Android

    当前场景
    现在安卓设备应用越来越广泛,有些出厂前可能没有预留系统OTA升级的入口,但是有app的升级通道,而又在一些情况下需要更改已经出厂的系统默认桌面,所以针对这种情况需要实现一种方案,在不进行系统升级更改的情况下,只通过升级App的方式,实现动态替换默认Launcher的方案
    技术方案
    三种方案
    用代码设置系统配置,保留两个Launcher应用进行动态切换

    动态删除添加系统默认/system/priv-app/目录下的默认Launcher.apk文件

    不设置Launcher,通过获取系统底层对Activity状态的监控,拦截Launcher

    前三种方案都需要系统权限,并在AndroidManifest.xml 中配置Launcher属性

    系统权限,配置shareUserId,并且用系统签名文件进行签名
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test.launcher"
    android:sharedUserId="android.uid.system">
     
    新增Launcher属性
    <activity android:name=".MainActivity">
    <intent-filter>
    <category android:name="android.intent.category.HOME" />
    <category android:name="android.intent.category.DEFAULT" />

    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    </acivity>
     
    方案一
    使用代码配置动态Launcher,需要额外的添加一个系统级别的权限

    <permission android:name="android.permission.SET_PREFERRED_APPLICATIONS"/>
    1
    该权限允许应用程序,进行系统属性的配置更改,有了该权限后,才可以进行动态的属性配置。


    清除默认Launcher:

    安装运行自定义Launcher应用
    遍历所有配置Launcher属性的应用
    清除除所有应用Launcher属性
    添加自定义桌面应用Launcher属性配置
    private void clear(Context context){

    ArrayList<IntentFilter> intentList = new ArrayList<>();
    ArrayList<ComponentName> cnList = new ArrayList<>();
    context.getPackageManager().getPreferredActivities(intentList, cnList, null);
    for(int i = 0; i < cnList.size(); i++) {
    IntentFilter dhIF = intentList.get(i);
    if(dhIF.hasAction(Intent.ACTION_MAIN) && dhIF.hasCategory(Intent.CATEGORY_HOME)) {//遍历过滤所有launcher
    try {
    String name = cnList.get(i).getPackageName();
    //清除原有的默认launcher
    context.getPackageManager().clearPackagePreferredActivities(name;
    }catch (Exception ex){
    }
    }
    }
    IntentFilter filter = new IntentFilter();
    filter.addAction(Intent.ACTION_MAIN);
    filter.addCategory(Intent.CATEGORY_HOME);
    filter.addCategory(Intent.CATEGORY_DEFAULT);

    final int N = mHome.size();
    ComponentName[] set = new ComponentName[N];
    for (int i = 0; i < N; i++) {
    ResolveInfo r = mHome.get(i);
    set[i] = new ComponentName(r.activityInfo.packageName,r.activityInfo.name);
    }
    //设置自定义launcher
    ComponentName launcher = new ComponentName(hiBoxLauncher, hiBoxActivity);
    context.getPackageManager().addPreferredActivity(filter, 1081344, set, launcher);

    恢复默认Launcher方法同上一样,清除完所有Launcher相关的意图配置,最后添加上系统Launcher包名。

    方案二
    通过文件删除的方式进行Launcher替换,需要文件的读写权限,而且系统应用删除后需要重启才能生效,所以还需要reboot的权限

    <permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <permission android:name="android.permission.REBOOT"/>
     
    清除默认Launcher:

    安装运行自定义Launcher应用
    复制/system/priv-app/Launcher2.apk保存到sdcrad
    删除/system/priv-app/Launcher2.apk
    重启安卓系统,只留下自定义Launcher应用生效
    恢复默认launcher只需要将事先保存的apk,拷贝到系统目录即可,当前系统会存在两个Launcher,需要手动选择一次默认Launcher

    方案三
    不需要替换Launcher,通过反射的方式注册系统android.app.
    IActivityController的隐藏aidl回调。监听每个应用的Activity界面的状态,当回调系统显示Launcher的时候拦截回调,打开自定义的应用界面,覆盖系统Launcher,需要系统权限,还需要额外的Activity设置权限

    <uses-permission android:name="android.permission.SET_ACTIVITY_WATCHER" />
    1
    1.声明aidl文件
    在工程中声明一个android.app.IActivityController的aidl,用于注册系统的回调,可以直接从Android源码中拷贝


    interface IActivityController{

    boolean activityStarting(in Intent intent, String pkg);

    boolean activityResuming(String pkg);

    boolean appCrashed(String processName, int pid,
    String shortMsg, String longMsg,
    long timeMillis, String stackTrace);

    int appEarlyNotResponding(String processName, int pid, String annotation);

    int appNotResponding(String processName, int pid, String processStats);

    2.反射注册aidl回调代码:


    //在进入页面时调用setActivityController()方法,注册aidl回调,当界面有变化时会触发activityStarting(),activityResuming()事件
    public void setActivityController() {
    Log.d(TAG,">>>>>>0519,");
    try {
    Class<?> cActivityManagerNative = Class.forName("android.app.ActivityManagerNative");
    Method mGetDefault = cActivityManagerNative.getMethod("getDefault", new Class[]{});
    Object oActivityManagerNative = mGetDefault.invoke(null, new Object[]{});
    Method mSetActivityController = cActivityManagerNative.getMethod("setActivityController",
    Class.forName("android.app.IActivityController"));
    mSetActivityController.invoke(oActivityManagerNative, new ActivityController());
    } catch (ClassNotFoundException e) {
    exceptionWhenSet(e);

    3.拦截Launcher
    绑定服务,获取系统回调的Activity状态接口,进行应用打开操作

    private class ActivityController extends android.app.IActivityController.Stub {

    public boolean activityStarting(Intent intent, String pkg) {

    if(launcher2.equals(pkg)&&isFilter){
    startHiBoxLauncher(); }
    return true;
    }
    public boolean activityResuming(String pkg) {
    Log.d(TAG,">>>>>>0519,pkg=" + pkg);
    if(launcher2.equals(pkg)&&isFilter){
    startHiBoxLauncher();
    }
    return true;
    }

    如果是管理员进行操作,需要恢复Launcher,可以用过标志位Boolen值来控制是否拦截系统Launcher

    风险
    1.重启安卓设备Launcher配置重置,方案一代码设置生效后,不需要手动进行选择,可以同时在两个launcher中切换,但是测试发现,安卓设备重启后,会清除配置,需要重新选择默认Launcher


    2.Android系统应用无法删除,在一些设备上测试发现,即使有了系统权限,只能对/system/priv-app/Launcher2.apk进行拷贝操作,无法进行删除操作。


    3.签名不一致覆盖安装风险,所有的方案都需要重新系统签名,获取系统权限,跟之前Android应用签名不一致,可能会出现覆盖安装失败的问题


    ————————————————
    版权声明:本文为CSDN博主「ArrayZhang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/rui1605/article/details/87808581/

  • 相关阅读:
    typedef void (*funcptr)(void) typedef void (*PFV)(); typedef int32_t (*PFI)();
    STM32 STM32F4 寄存器怎么配置不上, 无法往寄存器写入数据
    GPIO
    JSP和selevt 生命周期详解(JSP的生命周期和select很像,jsp底层就是一个selevt)
    jquery自带的排序方法(js也是)
    GET和POST是HTTP请求的两种基本方法,区别是什么!?
    springboot特性
    restful风格接口类型和优点
    提升必看!!!
    分组函数 partition by 的详解,与order by 区别
  • 原文地址:https://www.cnblogs.com/javalinux/p/14722640.html
Copyright © 2011-2022 走看看