• Android 手机应用


    Android 手机应用分身

    1、实现手机应用分身的方式

    当前市面上实现手机分身的方式主要有三类:

    1. 修改Framework -> 使用Android多用户机制进行实现

      该方式适用于手机厂商,修改底层代码,通过创建多用户的方法来实现手机分身功能。

      通过getFileDir()的api发现,在本体得到的是 data/data/com.xxx.xxx,克隆得到的是data/user/10/com.xxx.xxx

    2. 修改apk

      通过反编译apk,修改apk的包名、签名等将apk伪装成另一个app,市面上常见的第三方多开app大部分都是使用该技术。其特点是每次制作一个分身都需要时间进行一个拷贝、并且在应用列表中可以看到

      参考资料:

      https://blog.csdn.net/weixin_43970718/article/details/119116441

    3. 虚拟操作系统

      虚拟一个操作系统,克隆体app在这个虚拟系统中运行,在应用列表不可见,代表产品:360分身大师

      检测方案:
      这个据说是唯一一种绕过getFileDir()的分身方式,确实这种方式让我耗费了很长时间,下面以360的分身大师举例,详细说下分析过程

      1. 首先通过getFileDir()尝试,本体和克隆体输出一致。失败!!
      2. 通过在本体和克隆体登录不同账号获取SP中存储的账号信息,确定获取的信息不同,证明存储位置不一致
      3. 通过文件管理器查看最近修改的文件,发现系统根目录下有一个docker的文件夹,里面包含了跟根目录类似的结构,在其里面的Android/data下就会发现我们自己的包名,分身的本地数据就存放在这里。
      4. 找到分身的本地存储后,本想向其中存储数据用来标记是否为分身,但本体运行时报了一个无权限写入的异常,因此改为了判断当前是否有对该目录的写入权限

    2、Android 多用户

    2.1、多用户简介

    从Android 4.0开始,Google就开始在Android上布局多用户,UserManager因此而诞生。

    2.2、基础概念

    多用户的一些基础概念:

    1. Uid(用户id):在Linux上,一个用户Uid标识着一个给定的用户。Android上也沿用了Linux用户概念,Root用户Uid为0,System Uid为1000,并且,每个应用程序在安装时也被赋予了单独的Uid,这个Uid将伴随着应用从安装到卸载。
    2. Gid(用户组id):Linux上规定每个应用都应该有一个用户组,对于Android应用程序来说,每个应用的所属用户组与Uid相同。
    3. Gids:应用在安装后所获得权限的Id集合。在Android上,每个权限都可能对应一个或多个group,每个group有个gid name,gids就是通过每个gid name计算得出的id集合,一个UID可以关联GIDS,表明该UID拥有多种权限。

    2.3、多户用特性

    1. 独立的userId

      Android在创建每个用户时,都会分配一个整型的userId。对于主用户(正常下的默认用户)来说,userId为0,之后创建的userId将从10开始计算,每增加一个userId加1。

      K631:/ # pm list users
      Users:
              UserInfo{0:机主:c13} running
              UserInfo{10:clone:1010} running
      
      • 1
      • 2
      • 3
      • 4

      创建一个名为“ulangch”的用户:

      root@virgo:/ # pm create-user "ulangch"
      Success: created user id 11
      
      K631:/ # pm list users
      Users:
              UserInfo{0:机主:c13} running
              UserInfo{10:clone:1010} running
              UserInfo{11:ulangch:400} running
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8

      启动和切换到该用户:

      root@virgo:/ # am start-user 11
      Success: user started
      root@virgo:/ # am switch-user 11
      
      • 1
      • 2
      • 3
    2. 独立的文件存储

      为了多用户下的数据安全性,在每个新用户创建之初,不管是外部存储(External Storage)还是app data目录,Android都为其准备了独立的文件存储。

      多用户下的/storage分区:

      K631:/ # ls -l /storage/emulated
      total 9
      drwxrws--- 16 media_rw media_rw 3452 2022-09-08 17:44 0
      drwxrws--- 15 media_rw media_rw 3452 2022-09-14 10:10 10
      drwxrwx---  2 media_rw media_rw 3452 1970-01-01 08:03 obb
          
      K631:/ # ls -l /sdcard
      lrw-r--r-- 1 root root 21 2022-09-08 17:44 /sdcard -> /storage/self/primary
          
      K631:/ # ls -l /storage/self/primary
      lrwxrwxrwx 1 root root 19 2022-09-15 19:19 /storage/self/primary -> /storage/emulated/0
          
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12

      新用户创建时,Android在 “/storage/emulated” 目录下为每个用户都创建了名为用户id的目录,当我们在代码中使用 “Environment.getExternalStorageDirectory().absolutePath” 获取外部存储路径时,返回的就是当前用户下的对应目录(如:userId = 11, 则返回为 “/storage/emulated/10”)。

      另外,可以看出,我们平常说到的 “/sdcard” 目录其实最终也是软链到了 “/storage/emulated/0”

      多用户下的/data分区

      K631:/ # ls -l data/user/
      total 60
      drwxrwx--x 199 system system 24576 2022-09-15 11:13 0
      drwxrwx--x 182 system system 20480 2022-09-16 10:57 10
      drwxrwx--x 197 system system 24576 2022-09-16 11:00 11
          
      K631:/ # ls -l /data/user/10/com.tencent.mm/
      total 6
      drwxrws--x 2 u10_a148 u10_a148_cache 3452 2022-09-16 11:21 cache
      drwxrws--x 2 u10_a148 u10_a148_cache 3452 2022-09-16 11:21 code_cache
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

      与External Storage相同,新用户创建时,Android也会在 “data/user” 目录下创建了名为userId的目录,用于存储该用户中所有App的隐私数据,如果在代码中使用 “Context.getFilesDir()” 来获取应用的data目录,不同User下也会有不同。

      另外,也可以看出,平常说到的 “/data/data” 目录其实也是软链到了 “/data/user/0”。

      注:在Android中,应用的uid是和当前的用户有关的,同一个应用具有相同的appId,其uid的计算方式为: uid = userId * 1000000 + appId,在主用户中,uid = appId。

    3. 独立的权限控制

      1. 不同的用户具有权限不同,如:访客用户的默认权限限制就有:

        perseus:/ $ dumpsys user
        ...
          Guest restrictions:
            no_sms                        // 限制发送短信
            no_install_unknown_sources    // 限制安装
            no_config_wifi                // 限制配置WiFi
            no_outgoing_calls             // 限制拨打电话
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7

        (注:使用 “adb shell dumpsys user” 可以查看所有的用户信息,如userId、name、restrictions等)

        这些权限可以在创建用户时规定,也可以后期由系统动态设置。

      2. 不同用户下App的应用权限是独立的

        前面说到,uid与userId存在一种计算关系(uid = userId * 1000000 + appId),而在系统中对于权限控制也是根据uid和对应的userId来判定的,因此不同用户下相同应用可以具有不同的权限。

    4. App安装的唯一性

      App的文件存储和数据目录在不同用户下都是独立的,但是对于App的安装,多个用户下同一个App却保持着同一个安装目录,即:

      1. 普通三方app:/data/app/

      2. 普通系统应用:/system/app

      3. 特权系统应用:/system/priv-app

        K631:/ # ls /system/app/MTBFTool/
        MTBFTool.apk  oat
        
        • 1
        • 2

      拓展:权限在声明时安全等级(protectionLevel)分为3类:

      <permission android:name="android.permission.READ_EXTERNAL_STORAGE"
                  android:permissionGroup="android.permission-group.STORAGE"
                  android:label="@string/permlab_sdcardRead"
                  android:description="@string/permdesc_sdcardRead"
                  android:protectionLevel="dangerous" />
      
      <!-- Allows applications to access information about Wi-Fi networks.
               <p>Protection level: normal
      -->
      <permission android:name="android.permission.ACCESS_WIFI_STATE"
                  android:description="@string/permdesc_accessWifiState"
                  android:label="@string/permlab_accessWifiState"
                  android:protectionLevel="normal" />
      
      <!-- @SystemApi Allows applications to set the system time.
          <p>Not for use by third-party applications. -->
      <permission android:name="android.permission.SET_TIME"
                  android:protectionLevel="signature|privileged" />
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      1. normal:普通权限,在AndroidManifest.xml中声明就可以获取权限,如 INTERNET 权限
      2. dangerous:敏感权限,需要动态申请告知用户才能获取
      3. singature|privileged:具有系统签名的系统应用才可以获取的权限,对应上方的/system/priv-app

      因此,多用户下的应用其实只安装一次,不同用户下同一个应用的版本和签名都应该相同,不同用户下相同App能够独立运行是因为系统为他们创造了不同的运行环境和权限。

    5. kernel及系统进程的不变性

      在不同用户下,虽然能够看到不同的桌面,不同的运行环境,一切都感觉是新的,但是我们系统本身并没有发生改变,kernel进程、system_server进程以及所有daemon进程依然是同一个,并不会重启。

      而如果我们在不同用户中开启相同的app,我们可以看到可以有多个app进程,而他们的父进程都是同一个,即 zygote:

    参考资料:https://blog.csdn.net/misiyuan/article/details/77432020?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ETopNSimilar%7Edefault-1-77432020-blog-94654401.topnsimilarv1&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ETopNSimilar%7Edefault-1-77432020-blog-94654401.topnsimilarv1&utm_relevant_index=2

    3、UserManager

    多用户模型:

    framework/base/services/core/java/com/android/server/pm/UserManagerService.java
    framework/base/core/java/android/os/UserManager.java
    framework/base/core/java/android/content/pm/UserInfo.java
    framework/base/core/java/android/os/UserHandle.java
    
    • 1
    • 2
    • 3
    • 4

    链接:[多用户管理UserManager - Gityuan博客 | 袁辉辉的技术博客](http://gityuan.com/2016/11/20/user_manager/)

    3.1、概述

    Android 多用户模型,通过UserManagerService对多用户进行创建、删除、查询等管理操作。

    • Binder服务端:UserManagerService继承于IUserManager.Stub,作为 binder 服务端。
    • Binder客户端:UserManager的成员变量mService继承于IUserManager.Stub.Proxy,该变量作为binder客户端。UserManager的大部分核心工作都是交由其成员变量mService,再通过binder调用到UserManagerService所相应的方法。
    3.1.1、概念

    userId,uid,appid,ShareAppGid概念关系

    • userId:是指用户Id;
    • appId: 是指跟用户空间无关的应用程序id;取值范围 0<= appId <100000
    • uid:是指跟用户空间紧密相关的应用程序id;
    • SharedAppGid:是指可共享的应用id;

    转换关系:

    uid = userId * 100000  + appId
    SharedAppGid = 40000   + appId
    
    • 1
    • 2

    另外,PER_USER_RANGE = 100000, 意味着每个user空间最大可以有100000个appid。这些区间分布如下:

    • system appid: [1000, 9999]
    • application appid:[10000, 19999]
    • Shared AppGid: [50000, 59999]
    • isolated appid: [99000, 99999]
    3.1.2、UserHandle

    常见方法:

    方法含义
    isSameUser比较两个uid的userId是否相同
    isSameApp比较两个uid的appId是否相同
    isAppappId是否属于区间[10000,19999]
    isIsolatedappId是否属于区间[99000,99999]
    getIdentifier获取UserHandle所对应的userId

    常见成员变量:(UserHandle的成员变量mHandle便是userId)

    userId赋值含义
    USER_OWNER0拥有者
    USER_ALL-1所有用户
    USER_CURRENT-2当前活动用户
    USER_CURRENT_OR_SELF-3当前用户或者调用者所在用户
    USER_NULL-1000未定义用户

    类成员变量:

    UserHandle OWNER = new UserHandle(USER_OWNER); // 0
    UserHandle ALL = new UserHandle(USER_ALL); // -1
    UserHandle CURRENT = new UserHandle(USER_CURRENT); // -2
    UserHandle CURRENT_OR_SELF = new UserHandle(USER_CURRENT_OR_SELF); // -3
    
    • 1
    • 2
    • 3
    • 4

    关于UID默认情况下,客户端可使用rocess.myUserHandle(); 服务端可使用UserHandle.getCallingUserId();

    3.1.3、UserInfo

    UserInfo代表的是一个用户的信息,涉及到的flags及其含义,如下:

    flags含义
    FLAG_PRIMARY主用户,只有一个user具有该标识
    FLAG_ADMIN具有管理特权的用户,例如创建或删除其他用户
    FLAG_GUEST访客用户,可能是临时的
    FLAG_RESTRICTED限制性用户,较普通用户具有更多限制,例如禁止安装app或者管理wifi等
    FLAG_INITIALIZED表明用户已初始化
    FLAG_MANAGED_PROFILE表明该用户是另一个用户的轮廓
    FLAG_DISABLED表明该用户处于不可用状态
    3.1.4、UserState
    //用户启动中
    public final static int STATE_BOOTING = 0;
    //用户正常运行状态
    public final static int STATE_RUNNING = 1;
    //用户正在停止中
    public final static int STATE_STOPPING = 2;
    //用户处于关闭状态
    public final static int STATE_SHUTDOWN = 3;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    用户生命周期线:

    STATE_BOOTING -> STATE_RUNNING -> STATE_STOPPING -> STATE_SHUTDOWN.
    
    • 1

    3.2、UserManager的使用

    //获取UserManager
    UserManager mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
    //获取所有的用户信息
    List<UserInfo> users = UserManager.getUsers(true);
    //当前用户的id
    int myUserId = UserHandle.myUserId();
    //根据id获取当前用户的信息
    UserInfo myUserInfo = mUserManager.getUserInfo(myUserId);
    //用户icon的路径
    String iconPath = myUserInfo.iconPath;
    //用户icon的Bitmap
    Bitmap b = mUserManager.getUserIcon(myUserId);
    //当前用户是访客
    boolean mIsGuest = myUserInfo.isGuest();
    //当前用户是管理员,只有管理员添加或者移除其他用户
    boolean mIsAdmin = myUserInfo.isAdmin();
    //当前用户是否是受限制的
    boolean isRestricted = myUserInfo.isRestricted()//UserManager是否支持切换用户
    boolean canSwitchUsers = mUserManager.canSwitchUsers();
     
    //循环遍历所有的用户
    for (UserInfo user : users) {
        //该用户必须是要支持被切换的
        if (!user.supportsSwitchToByUser()) {
            continue;
        }
        if (user.id == UserHandle.myUserId()) {//当前用户的信息
     
        } else if (user.isGuest()) {//当前用户是否是访客
            continue;
        } else {
            if (user.isAdmin()) {//如果用户是管理,那就显示管理员
                
            }
        }
        //当前用户是否被初始化,如果没有初始化,可以做一次初始化
        if (!isInitialized(user)) {
     
        } else if (user.isRestricted()) {//用户是受限制的
     
        }
    }
     
    //监听用户的移除,添加和切换
    IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
    filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
    context.registerReceiverAsUser(mUserChangeReceiver, UserHandle.ALL, filter, null, mHandler);
     
    private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {//用户被移除
                ............
            } else if (intent.getAction().equals(Intent.ACTION_USER_INFO_CHANGED)) {//用户信息改变
                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                ............
            }
        }
    };
     
    //创建受限制的用户
    UserInfo newUserInfo = mUserManager.createRestrictedProfile(mAddingUserName);
     
    //创建用户
    UserInfo newUserInfo = mUserManager.createUser(mAddingUserName, 0);
     
    //移除当前用户,还需要再指定系统用户;
    ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
    mUserManager.removeUser(UserHandle.myUserId());
     
    //切换用户
    ActivityManager.getService().switchUser(userId);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    4、逻辑实现

    4.1、进入设置中的应用界面逻辑代码

    AppDashboardFragment.java,通过getPreferenceScreenResId()进入应用界面的布局文件app.xml

    /*
     * Copyright (C) 2021 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.android.settings.applications;
    
    import android.app.settings.SettingsEnums;
    import android.content.Context;
    import android.provider.SearchIndexableResource;
    
    import com.android.settings.R;
    import com.android.settings.dashboard.DashboardFragment;
    import com.android.settings.search.BaseSearchIndexProvider;
    import com.android.settingslib.core.AbstractPreferenceController;
    import com.android.settingslib.search.SearchIndexable;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    /** Settings page for apps. */
    @SearchIndexable
    public class AppDashboardFragment extends DashboardFragment {
    
        private static final String TAG = "AppDashboardFragment";
        private AppsPreferenceController mAppsPreferenceController;
    
        private static List<AbstractPreferenceController> buildPreferenceControllers(Context context) {
            final List<AbstractPreferenceController> controllers = new ArrayList<>();
            controllers.add(new AppsPreferenceController(context));
            return controllers;
        }
    
        @Override
        public int getMetricsCategory() {
            return SettingsEnums.MANAGE_APPLICATIONS;
        }
    
        @Override
        protected String getLogTag() {
            return TAG;
        }
    
        @Override
        public int getHelpResource() {
            return R.string.help_url_apps_and_notifications;
        }
    
        @Override
        protected int getPreferenceScreenResId() {
            return R.xml.apps;
        }
    
        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
            use(SpecialAppAccessPreferenceController.class).setSession(getSettingsLifecycle());
            mAppsPreferenceController = use(AppsPreferenceController.class);
            mAppsPreferenceController.setFragment(this /* fragment */);
            getSettingsLifecycle().addObserver(mAppsPreferenceController);
    
            final HibernatedAppsPreferenceController hibernatedAppsPreferenceController =
                    use(HibernatedAppsPreferenceController.class);
            getSettingsLifecycle().addObserver(hibernatedAppsPreferenceController);
        }
    
        @Override
        protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
            return buildPreferenceControllers(context);
        }
    
        public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
                new BaseSearchIndexProvider() {
                    @Override
                    public List<SearchIndexableResource> getXmlResourcesToIndex(
                            Context context, boolean enabled) {
                        final SearchIndexableResource sir = new SearchIndexableResource(context);
                        sir.xmlResId = R.xml.apps;
                        return Arrays.asList(sir);
                    }
    
                    @Override
                    public List<AbstractPreferenceController> createPreferenceControllers(
                            Context context) {
                        return buildPreferenceControllers(context);
                    }
                };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    
    
    
    <PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:settings="http://schemas.android.com/apk/res-auto"
        android:key="apps_screen"
        android:title="@string/apps_dashboard_title">
    
        <Preference
            android:key="all_app_infos"
            android:title="@string/all_apps"
            android:summary="@string/summary_placeholder"
            android:order="-999"
            android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
            settings:keywords="@string/keywords_applications_settings"/>
    	
        
        <PreferenceCategory
            android:key="recent_apps_category"
            android:title="@string/recent_app_category_title"
            android:order="-998"
            settings:searchable="false">
            
    
            
            <Preference
                android:key="see_all_apps"
                android:title="@string/default_see_all_apps_title"
                android:icon="@drawable/ic_chevron_right_24dp"
                android:fragment="com.android.settings.applications.manageapplications.ManageApplications"
                android:order="5"
                settings:searchable="false">
            Preference>
        PreferenceCategory>
    
        <PreferenceCategory
            android:key="general_category"
            android:title="@string/category_name_general"
            android:order="-997"
            android:visibility="gone"
            settings:searchable="false"/>
    
        <Preference
            android:key="default_apps"
            android:title="@string/app_default_dashboard_title"
            android:order="-996"
            settings:controller="com.android.settings.applications.DefaultAppsPreferenceController">
            <intent android:action="android.settings.MANAGE_DEFAULT_APPS_SETTINGS"/>
        Preference>
    
        <Preference
            android:key="game_settings"
            android:title="@string/game_settings_title"
            android:summary="@string/game_settings_summary"
            android:order="-995"
            settings:controller="com.android.settings.applications.GameSettingsPreferenceController">
        Preference>
    
        <PreferenceCategory
            android:key="dashboard_tile_placeholder"
            android:order="10"/>
    
        <Preference
            android:key="hibernated_apps"
            android:title="@string/unused_apps"
            android:summary="@string/summary_placeholder"
            android:order="15"
            settings:keywords="app_hibernation_key"
            settings:controller="com.android.settings.applications.HibernatedAppsPreferenceController">
            <intent android:action="android.intent.action.MANAGE_UNUSED_APPS"/>
        Preference>
    
        <Preference
            android:key="special_access"
            android:fragment="com.android.settings.applications.specialaccess.SpecialAccessSettings"
            android:title="@string/special_access"
            android:order="20"
            settings:controller="com.android.settings.applications.SpecialAppAccessPreferenceController"/>
    	
        
        <Preference
            android:key="parallel_app"
            android:fragment="com.android.settings.applications.parallelapp.ParallelAppSettings"
            android:title="@string/parallel_app"
            android:order="25"
            settings:controller="com.android.settings.applications.parallelapp.ParallelAppPreferenceController"/>
    PreferenceScreen>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102

    在这里插入图片描述

    在这里插入图片描述

    4.2、进入手机分身的fragment类

    ParallelAppSettings.java

    /*
     * Copyright (C) 2016 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
     * except in compliance with the License. You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software distributed under the
     * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied. See the License for the specific language governing
     * permissions and limitations under the License.
     */
    
    package com.android.settings.applications.parallelapp;
    
    import android.app.ActivityManager;
    import android.app.IActivityManager;
    import android.app.settings.SettingsEnums;
    import android.content.Context;
    import android.content.pm.UserInfo;
    import android.os.Bundle;
    import android.os.RemoteException;
    import android.os.UserHandle;
    import android.os.UserManager;
    import android.widget.Switch;
    import android.util.Log;
    
    import androidx.preference.PreferenceGroup;
    
    import com.android.settings.R;
    import com.android.settings.dashboard.DashboardFragment;
    import com.android.settings.SettingsActivity;
    import com.android.settingslib.applications.ApplicationsState;
    import com.android.settingslib.applications.ApplicationsState.AppFilter;
    
    import java.util.List;
    
    public class ParallelAppSettings extends DashboardFragment {
        private static final String TAG = "ParallelAppSettings";
        private static final String KEY = "app_list";
        private AppFilter mFilter;
        private int mCloneUserId = -1;
        private IActivityManager mAms;
        private UserManager mUm;
        PreferenceGroup mAppList;
    
        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
            mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
                mFilter = ApplicationsState.FILTER_ALL_ENABLED;
            use(ManageParallelAppsPreferenceController.class).setSession(getSettingsLifecycle());
            use(ManageParallelAppsPreferenceController.class).setParentFragment(this);
            use(ManageParallelAppsPreferenceController.class).setFilter(mFilter);
            mAms = ActivityManager.getService();
        }
    
        @Override
        public void onCreate(Bundle icicle) {
            super.onCreate(icicle);
            // 获取布局文件中PreferenceCategory中key
            mAppList = (PreferenceGroup) findPreference(KEY);
            android.util.Log.d("xin.wang", " Content of mAppList : " + mAppList.getPreferenceCount());
            // 视图反射进入ManageParallelAppsPreferenceController,设置setPreferenceGroup
            use(ManageParallelAppsPreferenceController.class).setPreferenceGroup(mAppList);
        }
    
        @Override
        public void onResume() {
            super.onResume();
            use(ManageParallelAppsPreferenceController.class).rebuild();
        }
    
        @Override
        protected String getLogTag() {
            return TAG;
        }
    
        @Override
        protected int getPreferenceScreenResId() {
            return R.xml.parallel_app_settings;
        }
    
        @Override
        public int getMetricsCategory() {
            return SettingsEnums.PAGE_UNKNOWN;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89

    user()方法,是父类DashboardFragment的方法

     protected <T extends AbstractPreferenceController> T use(Class<T> clazz) {
             List<AbstractPreferenceController> controllerList = mPreferenceControllers.get(clazz);
              if (controllerList != null) {
                  if (controllerList.size() > 1) {
                      Log.w(TAG, "Multiple controllers of Class " + clazz.getSimpleName()
                              + " found, returning first one.");
                  }
                  return (T) controllerList.get(0);
              }
      
              return null;
          }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    进入布局文件parallel_app_settings.xml

    <PreferenceScreen
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:settings="http://schemas.android.com/apk/res-auto"
        android:key="parallel_app_settings"
        android:title="@string/parallel_app"
        settings:controller="com.android.settings.applications.parallelapp.ManageParallelAppsPreferenceController"
        settings:searchable="false">
    
        <com.android.settingslib.widget.TopIntroPreference
             android:key="support_app"
             android:title="@string/parallel_app_support"/>
    
        <PreferenceCategory
            android:key="app_list"
            android:order="10"
            settings:searchable="false">
        PreferenceCategory>
    
    PreferenceScreen>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    进入ManageParallelAppsPreferenceController
    /*
     * Copyright (C) 2017 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.android.settings.applications.parallelapp;
    
    import android.app.ActivityManager;
    import android.app.Application;
    import android.content.Context;
    import android.content.pm.PackageInstaller;
    import android.content.pm.PackageManager;
    import android.os.ServiceManager;
    import android.os.UserHandle;
    import android.os.UserManager;
    
    import androidx.preference.Preference;
    import androidx.preference.PreferenceScreen;
    import androidx.preference.PreferenceGroup;
    
    import com.android.settings.applications.AppStateBaseBridge;
    import com.android.settings.core.BasePreferenceController;
    import com.android.settings.dashboard.DashboardFragment;
    import com.android.settings.R;
    import com.android.settings.Utils;
    import com.android.settingslib.applications.ApplicationsState;
    import com.android.settingslib.applications.ApplicationsState.AppEntry;
    import com.android.settingslib.applications.ApplicationsState.AppFilter;
    import com.android.settingslib.core.lifecycle.Lifecycle;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.Set;
    import java.util.TreeSet;
    
    public class ManageParallelAppsPreferenceController extends BasePreferenceController implements
            ApplicationsState.Callbacks, Preference.OnPreferenceChangeListener {
        private static final String TAG = "ManageParallelAppsPreferenceController";
        private final ApplicationsState mApplicationsState;
        private final PackageInstaller mPackageInstaller;
        private final PackageManager mPm;
        private final UserManager mUm;
        private ApplicationsState.Session mSession;
        private AppFilter mFilter;
        private Context mContext;
        private ParallelAppSettings mParentFragment;
        private HashSet<String> mSupportAppClone;
        PreferenceGroup mAppList;
    
        public ManageParallelAppsPreferenceController(Context context, String key) {
            super(context, key);
    
            mContext = context;
            mApplicationsState = ApplicationsState.getInstance(
                    (Application) context.getApplicationContext());
            mPm = mContext.getPackageManager();
            mPackageInstaller = mPm.getPackageInstaller();
            mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
            //get app could cloning by -> com.tencent.mm=>微信,com.sina.weibo=>微博,com.tencent.mobilqq=>QQ,com.ss.android.ugc.aweme=>抖音
            // 读取支持手机分身的应用包名
            String[] supportApps = context.getResources().getStringArray(com.unisoc.internal.R.array.support_app_clone);
            // 将数组转化为列表
            mSupportAppClone = new HashSet<String>(Arrays.asList(supportApps));
        }
    
        @Override
        public int getAvailabilityStatus() {
            ActivityManager activityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
            boolean isAdmin = mUm.isAdminUser();
            boolean isLowRam = activityManager.isLowRamDevice();
            boolean isSupportAppClone = mContext.getResources().getBoolean(R.bool.config_support_appclone);
    
            return isAdmin && !isLowRam && isSupportAppClone ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
        }
    
        @Override
        public void onRebuildComplete(ArrayList<AppEntry> apps) {
            if (apps == null) {
                return;
            }
    
            final Set<String> appsKeySet = new TreeSet<>();
            final int N = apps.size();
            android.util.Log.d("xin.wang", "apps.size -> " + N);
            for (int i = 0; i < N; i++) {
                final AppEntry entry = apps.get(i);
                android.util.Log.d("xin.wang", "AppEntry entry --> " + entry.toString());
                boolean isSupportClone = mSupportAppClone.contains(entry.info.packageName);
                android.util.Log.d("xin.wang", "get Should Clone for clone --> " + mSupportAppClone);
                android.util.Log.d("xin.wang", "get app packageName : -> " + entry.info.packageName + "<- isSupportClone ->" + isSupportClone );
                int userId = UserHandle.getUserId(entry.info.uid);
                boolean isAdminUser = mUm.isUserAdmin(userId);
    
                if (mSupportAppClone.contains(entry.info.packageName) && isAdminUser) {
                    if (!shouldAddPreference(entry)) {
                        continue;
                    }
                    final String prefkey = ParallelAppPreference.generateKey(entry);
                    appsKeySet.add(prefkey);
                    android.util.Log.d("xin.wang", " get appsKeySet -> " + appsKeySet + "<- prefkey ->" + prefkey);
                    // 获取布局中key
                    ParallelAppPreference preference =
                            (ParallelAppPreference) mAppList.findPreference(prefkey);
                    // 当布局为空的时候,实例化一个布局
                    if (preference == null) {
                        preference = new ParallelAppPreference(mContext, entry,
                                mApplicationsState, mUm, mPm, mPackageInstaller);
                        preference.setOnPreferenceChangeListener(this);
                        mAppList.addPreference(preference);
                    } else {
                        preference.updateState();
                    }
                    preference.setOrder(i);
                }
            }
    
            removeUselessPrefs(appsKeySet);
        }
    
        @Override
        public void onRunningStateChanged(boolean running) {}
    
        @Override
        public void onPackageListChanged() {
            rebuild();
        }
    
        @Override
        public void onPackageIconChanged() {}
    
        @Override
        public void onPackageSizeChanged(String packageName) {}
    
        @Override
        public void onAllSizesComputed() {}
    
        @Override
        public void onLauncherInfoChanged() {}
    
        @Override
        public void onLoadEntriesCompleted() {
            rebuild();
        }
    
        @Override
        public boolean onPreferenceChange(Preference preference, Object newValue) {
            if (Utils.isMonkeyRunning()) {
                return true;
            }
            if (preference instanceof ParallelAppPreference) {
                ParallelAppPreference parAppPreference = (ParallelAppPreference)preference;
                if ((Boolean)newValue) {
                    parAppPreference.openParApp();
                } else {
                    parAppPreference.createCloseOptionsDialog(mParentFragment.getActivity());
                }
            }
            return true;
        }
    
        public void setFilter(AppFilter filter) {
            mFilter = filter;
        }
    
        public void setSession(Lifecycle lifecycle) {
            mSession = mApplicationsState.newSession(this, lifecycle);
        }
    
        public void setParentFragment(ParallelAppSettings parentFragment) {
            mParentFragment = parentFragment;
        }
    
        public void setPreferenceGroup(PreferenceGroup appList) {
            mAppList = appList;
            android.util.Log.d("xin.wang", "setPreferenceGroup: -> appList -> " + appList.getPreferenceCount());
        }
    
        private boolean shouldAddPreference(AppEntry app) {
            return app != null && UserHandle.isApp(app.info.uid);
        }
    
        private void removeUselessPrefs(final Set<String> appsKeySet) {
            final int prefCount = mAppList.getPreferenceCount();
            android.util.Log.d("xin.wang", "<- prefCount ->  " + prefCount);
            String prefKey;
            if (prefCount > 0) {
                for (int i = prefCount - 1; i >= 0; i--) {
                    Preference pref = mAppList.getPreference(i);
                    prefKey = pref.getKey();
                    boolean isEmpty = !appsKeySet.isEmpty();
                    boolean contains = appsKeySet.contains(prefKey);
                    android.util.Log.d("xin.wang", "<- isEmpty -> " + isEmpty + "<- contains -> " + contains + "<- appsKeySet -> " + appsKeySet + "<- preKey ->" + prefKey);
                    if (!appsKeySet.isEmpty() && appsKeySet.contains(prefKey)) {
                        continue;
                    }
                    mAppList.removePreference(pref);
                }
            }
        }
    
        public void rebuild() {
            final ArrayList<AppEntry> apps = mSession.rebuild(mFilter,
                    ApplicationsState.ALPHA_COMPARATOR);
            boolean flag = apps != null;
            if (apps != null) {
                android.util.Log.d("xin.wang", "rebuild: <-- apps -> " + apps.size());
                onRebuildComplete(apps);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    进入分身的布局ParallelAppPreference.java

    点击swicth开启分身的时候创建一个Toast来提醒
    在这里插入图片描述

    关闭分身弹出dialog,判断是否进行关闭

    在这里插入图片描述

    /*
     * Copyright (C) 2017 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.android.settings.applications.parallelapp;
    
    import android.app.Activity;
    import android.app.ActivityManager;
    import android.app.IActivityManager;
    import android.content.Context;
    import android.content.DialogInterface;
    import android.content.Intent;
    import android.content.pm.ApplicationInfo;
    import android.content.pm.IPackageManager;
    import android.content.pm.PackageInstaller;
    import android.content.pm.PackageManager;
    import android.content.pm.PackageManager.NameNotFoundException;
    import android.content.pm.UserInfo;
    import android.os.RemoteException;
    import android.os.ServiceManager;
    import android.os.UserHandle;
    import android.os.UserManager;
    import android.util.Log;
    import android.view.View;
    import android.widget.Toast;
    
    import androidx.appcompat.app.AlertDialog;
    import androidx.preference.DialogPreference;
    import androidx.preference.PreferenceViewHolder;
    
    import com.android.settings.R;
    import com.android.settingslib.applications.ApplicationsState;
    import com.android.settingslib.applications.ApplicationsState.AppEntry;
    import com.android.settingslib.widget.AppSwitchPreference;
    
    import java.util.List;
    
    public class ParallelAppPreference extends AppSwitchPreference {
        private final ApplicationsState mApplicationsState;
        private final AppEntry mEntry;
        private final PackageInstaller mPackageInstaller;
        private final PackageManager mPm;
        private final String TAG = "ParallelAppPreference";
        private final UserManager mUm;
        private Activity mActivity;
        private Context mContext;
    
        public ParallelAppPreference (final Context context, AppEntry entry,
                ApplicationsState applicationsState, UserManager um, PackageManager pm, PackageInstaller pi) {
            super(context);
            mContext = context;
            setWidgetLayoutResource(R.layout.restricted_switch_widget);
            mEntry = entry;
            mEntry.ensureLabel(context);
            mApplicationsState = applicationsState;
            mPm = pm;
            mPackageInstaller = pi;
            mUm = um;
            setKey(generateKey(mEntry));
            if (mEntry.icon == null) {
                mApplicationsState.ensureIcon(mEntry);
            }
            setIcon(mEntry.icon);
            setTitle(mEntry.label);
            updateState();
        }
    
        static String generateKey(final AppEntry entry) {
            android.util.Log.d("xin.wang", "generateKey: -> AppEntry ->  " + entry.info.packageName + " | " + entry.info.uid);
            return entry.info.packageName + "|" + entry.info.uid;
        }
    
        @Override
        public void onBindViewHolder(PreferenceViewHolder holder) {
            if (mEntry.icon == null) {
                holder.itemView.post(new Runnable() {
                    @Override
                    public void run() {
                        mApplicationsState.ensureIcon(mEntry);
                        setIcon(mEntry.icon);
                    }
                });
            }
            final View widgetFrame = holder.findViewById(android.R.id.widget_frame);
            widgetFrame.setVisibility(View.VISIBLE);
            super.onBindViewHolder(holder);
    
            holder.findViewById(R.id.restricted_icon).setVisibility(View.GONE);
            holder.findViewById(android.R.id.switch_widget).setVisibility(View.VISIBLE);
        }
    	
        // 更新状态
        public void updateState() {
            boolean isChecked = false;
            boolean hasCloneUser = hasCloneUser();
            try {
                if (hasCloneUser) {
                    ApplicationInfo info = mPm.getApplicationInfoAsUser(
                                    mEntry.info.packageName, PackageManager.GET_META_DATA, getCloneUser());
                    isChecked = (info.flags & ApplicationInfo.FLAG_INSTALLED) != 0 ? true : false;
                    android.util.Log.d("xin.wang", "updateState -> 1 -> : " + isChecked);
                }
                android.util.Log.d("xin.wang", "updateState -> 2 -> : " + isChecked);
                setChecked(isChecked);
            } catch (NameNotFoundException e) {
                 Log.e(TAG,"can not found Application "+mEntry.label);
            }
        }
        //关闭应用分身的时候弹出的dialog -> parallel_app_close -> 当关闭时分身应用的所有数据将会被清除。您确定要继续吗?
        public void createCloseOptionsDialog(Activity activity) {
            mActivity = activity;
            AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
            builder.setMessage(mContext.getResources().getString(R.string.parallel_app_close));
            builder.setPositiveButton(android.R.string.ok, mDialogClickListener);
            builder.setNegativeButton(android.R.string.cancel, mDialogClickListener);
            AlertDialog dialog = builder.create();
            dialog.setCancelable(false);
            dialog.show();
        }
    	
        // 开始手机分身
        public void openParApp() {
            if (!hasCloneUser()) {
                createAndStartCloneUser();
            }
            try {
                //应用双开的方法 -> installExistingPackageAsUser(String packageName,int userId);
                int status = mPm.installExistingPackageAsUser(mEntry.info.packageName, getCloneUser());
                android.util.Log.d("xin.wang", "openParApp: -> status -> " + status + "<- mEntry.info.packageName -> " + mEntry.info.packageName);
                if (status == PackageManager.INSTALL_SUCCEEDED) {   //PackageManager.INSTALL_SUCCEEDED -> 返回安装成功的状态码 1
                    android.util.Log.d("xin.wang", "openParApp: -> status -> " + status);
                    createToast();
                } else {
                    Log.d(TAG, "open Parallel App failed status:"+status);
                }
            } catch (PackageManager.NameNotFoundException e) {
                Log.d(TAG, "open Parallel App failed ,"+e);
            }
        }
    
        // 创建开启分身的Toast -> 分身的 %1$s 已经创建
        private void createToast() {
            String content = String.format(mContext.getResources().getString(R.string.parallel_app_open), mEntry.label);
            Toast.makeText(mContext, content, Toast.LENGTH_LONG).show();
        }
    	
        // 获取克隆的用户
        private int getCloneUser() {
            int cloneUserId = UserHandle.myUserId();
            int userCount = 0;
            List<UserInfo> users = mUm.getUsers();  //UserManager.getUser() -> 获取所有的用户信息
            for(UserInfo user : users){
                userCount++;
                android.util.Log.d("xin.wang", "getCloneUser: users -> " + userCount);
                if (user.userType != null && user.userType.equals(UserManager.USER_TYPE_PROFILE_CLONE)) {
                    cloneUserId = user.id;
                }
            }
            return cloneUserId;
        }
    	
        //判断是否克隆
        private boolean hasCloneUser() {
            boolean hasCloneUser = false;
            List<UserInfo> users = mUm.getUsers();  // UserManager.getUsers() -> 获取所有的用户信息
            android.util.Log.d("xin.wang", "hasCloneUser: -> user -> " + users + "<- user.size() -> " + users.size());
            for(UserInfo user : users){
                android.util.Log.d("xin.wang", "hasCloneUser: -> user.userType -> " + user.userType);
                if (user.userType != null && user.userType.equals(UserManager.USER_TYPE_PROFILE_CLONE)) {
                    hasCloneUser = true;
                    break;
                }
            }
            return hasCloneUser;
        }
    	
        //创建第二个user用户
        private void createAndStartCloneUser() {
            String userName = mContext.getResources().getString(R.string.clone_user);
            UserInfo userInfo = mUm.createProfileForUser(userName, UserManager.USER_TYPE_PROFILE_CLONE,
                    UserInfo.FLAG_PROFILE, UserHandle.USER_SYSTEM);
            IActivityManager ams = ActivityManager.getService();
            try {
                ams.startUserInBackground(userInfo.getUserHandle().getIdentifier());
            } catch(RemoteException e) {
                Log.d(TAG, "create and start clone user failed");
                e.printStackTrace();
            }
        }
    	
        //关闭手机分身的时候弹出的dialog
        private DialogInterface.OnClickListener mDialogClickListener = new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface arg0, int arg1) {
                if (arg1 == DialogInterface.BUTTON_NEGATIVE) { //DialogInterface.BUTTON_NEGATIVE -> 取消按钮,点击dialog的取消按钮
                    setChecked(true);
                } else {
                    //deletePackageAsUser(String packageName,IPackageDeleteObserver observer,int Flags,int userId);
                    mPm.deletePackageAsUser(mEntry.info.packageName, null, 0, getCloneUser());
                    android.util.Log.d("xin.wang", "DialogInterface.OnClickListener ->  mEntry.info.packageName -> " + mEntry.info.packageName);
                }
            }
        };
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215

    4.3、计算应用界面中应用的数量

    在这里插入图片描述

    通过该界面的布局文件app.xml中的key-> recent_apps_category

    进入AppsPreferenceController.java类中,该类计算当前应用的数量,并且显示当前使用的应用数量

    通过分析,可知,该方法中的loadAllAppsCount()加载所有app的数量,该方法实例化InstalledAppCounter类,并重写onCountComplete()方法

    /*
     * Copyright (C) 2021 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.android.settings.applications;
    
    import android.app.Application;
    import android.app.usage.UsageStats;
    import android.content.Context;
    import android.icu.text.RelativeDateTimeFormatter;
    import android.os.UserHandle;
    import android.text.TextUtils;
    import android.util.ArrayMap;
    
    import androidx.annotation.VisibleForTesting;
    import androidx.fragment.app.Fragment;
    import androidx.lifecycle.Lifecycle;
    import androidx.lifecycle.LifecycleObserver;
    import androidx.lifecycle.OnLifecycleEvent;
    import androidx.preference.Preference;
    import androidx.preference.PreferenceCategory;
    import androidx.preference.PreferenceScreen;
    
    import com.android.settings.R;
    import com.android.settings.applications.appinfo.AppInfoDashboardFragment;
    import com.android.settings.core.BasePreferenceController;
    import com.android.settingslib.Utils;
    import com.android.settingslib.applications.ApplicationsState;
    import com.android.settingslib.utils.StringUtil;
    import com.android.settingslib.widget.AppPreference;
    
    import java.util.List;
    import java.util.Map;
    
    /**
     * This controller displays up to four recently used apps.
     * If there is no recently used app, we only show up an "App Info" preference.
     */
    public class AppsPreferenceController extends BasePreferenceController implements
            LifecycleObserver {
    
        public static final int SHOW_RECENT_APP_COUNT = 4;
    
        @VisibleForTesting
        static final String KEY_RECENT_APPS_CATEGORY = "recent_apps_category";
        @VisibleForTesting
        static final String KEY_GENERAL_CATEGORY = "general_category";
        @VisibleForTesting
        static final String KEY_ALL_APP_INFO = "all_app_infos";
        @VisibleForTesting
        static final String KEY_SEE_ALL = "see_all_apps";
    
        private final ApplicationsState mApplicationsState;
        private final int mUserId;
    
        @VisibleForTesting
        List<UsageStats> mRecentApps;
        @VisibleForTesting
        PreferenceCategory mRecentAppsCategory;
        @VisibleForTesting
        PreferenceCategory mGeneralCategory;
        @VisibleForTesting
        Preference mAllAppsInfoPref;
        @VisibleForTesting
        Preference mSeeAllPref;
    
        private Fragment mHost;
        private boolean mInitialLaunch = false;
    
        public AppsPreferenceController(Context context) {
            super(context, KEY_RECENT_APPS_CATEGORY);
            mApplicationsState = ApplicationsState.getInstance(
                    (Application) mContext.getApplicationContext());
            mUserId = UserHandle.myUserId();
        }
    
        public void setFragment(Fragment fragment) {
            mHost = fragment;
        }
    
        @Override
        public int getAvailabilityStatus() {
            return AVAILABLE_UNSEARCHABLE;
        }
    
        @Override
        public void displayPreference(PreferenceScreen screen) {
            super.displayPreference(screen);
            initPreferences(screen);
            refreshUi();
            mInitialLaunch = true;
        }
    
        @Override
        public void updateState(Preference preference) {
            super.updateState(preference);
            if (!mInitialLaunch) {
                refreshUi();
            }
        }
    
        /**
         * Called when the apps page pauses.
         */
        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        public void onPause() {
            mInitialLaunch = false;
        }
    
        @VisibleForTesting
        void refreshUi() {
            loadAllAppsCount();
            mRecentApps = loadRecentApps();
            android.util.Log.d("xin.wang", "mRecentApps -> : " + mRecentApps.size() + "<- loadAllAppsCount -> " + loadRecentApps());
            if (!mRecentApps.isEmpty()) {
                displayRecentApps();
                mAllAppsInfoPref.setVisible(false);
                mRecentAppsCategory.setVisible(true);
                mGeneralCategory.setVisible(true);
                mSeeAllPref.setVisible(true);
            } else {
                mAllAppsInfoPref.setVisible(true);
                mRecentAppsCategory.setVisible(false);
                mGeneralCategory.setVisible(false);
                mSeeAllPref.setVisible(false);
            }
        }
    	
        // 计算应用的数量
        @VisibleForTesting
        void loadAllAppsCount() {
            // Show total number of installed apps as See all's summary.
            new InstalledAppCounter(mContext, InstalledAppCounter.IGNORE_INSTALL_REASON,
                    mContext.getPackageManager()) {
                @Override
                protected void onCountComplete(int num) {
                    if (!mRecentApps.isEmpty()) {
                        mSeeAllPref.setTitle(
                                mContext.getResources().getQuantityString(R.plurals.see_all_apps_title,
                                        num, num));
                        android.util.Log.d("xin.wang", "onCountComplete: -> AppsCount -> " + num);
                    } else {
                        mAllAppsInfoPref.setSummary(mContext.getString(R.string.apps_summary, num));
                    }
                }
            }.execute();
        }
    
        @VisibleForTesting
        List<UsageStats> loadRecentApps() {
            final RecentAppStatsMixin recentAppStatsMixin = new RecentAppStatsMixin(mContext,
                    SHOW_RECENT_APP_COUNT);
            recentAppStatsMixin.loadDisplayableRecentApps(SHOW_RECENT_APP_COUNT);
            return recentAppStatsMixin.mRecentApps;
        }
    
        private void initPreferences(PreferenceScreen screen) {
            mRecentAppsCategory = screen.findPreference(KEY_RECENT_APPS_CATEGORY);
            mGeneralCategory = screen.findPreference(KEY_GENERAL_CATEGORY);
            mAllAppsInfoPref = screen.findPreference(KEY_ALL_APP_INFO);
            mSeeAllPref = screen.findPreference(KEY_SEE_ALL);
            mRecentAppsCategory.setVisible(false);
            mGeneralCategory.setVisible(false);
            mAllAppsInfoPref.setVisible(false);
            mSeeAllPref.setVisible(false);
        }
    
        private void displayRecentApps() {
            if (mRecentAppsCategory != null) {
                final Map<String, Preference> existedAppPreferences = new ArrayMap<>();
                final int prefCount = mRecentAppsCategory.getPreferenceCount();
                android.util.Log.d("xin.wang", "<- prefCount -> : " + prefCount);
                for (int i = 0; i < prefCount; i++) {
                    final Preference pref = mRecentAppsCategory.getPreference(i);
                    final String key = pref.getKey();
                   // android.util.Log.d("xin.wang", "for each key -> : " + key);
                    if (!TextUtils.equals(key, KEY_SEE_ALL)) {
                        existedAppPreferences.put(key, pref);
                    }
                    android.util.Log.d("xin.wang", "<- Map -> " + existedAppPreferences);
                }
    
                int showAppsCount = 0;
                android.util.Log.d("xin.wang", "List mRecentApps -> : " + mRecentApps);
                for (UsageStats stat : mRecentApps) {
                    final String pkgName = stat.getPackageName();
                    final ApplicationsState.AppEntry appEntry =
                            mApplicationsState.getEntry(pkgName, mUserId);
                    if (appEntry == null) {
                        continue;
                    }
    
                    boolean rebindPref = true;
                    Preference pref = existedAppPreferences.remove(pkgName);
                    if (pref == null) {
                        pref = new AppPreference(mContext);
                        rebindPref = false;
                    }
    
                    android.util.Log.d("xin.wang", "pkgName -> : " + pkgName + "<- appEntry.label -> : " + appEntry.label + "<- pref -> " + pref);
                    pref.setKey(pkgName);
                    pref.setTitle(appEntry.label);
                    pref.setIcon(Utils.getBadgedIcon(mContext, appEntry.info));
                    pref.setSummary(StringUtil.formatRelativeTime(mContext,
                            System.currentTimeMillis() - stat.getLastTimeUsed(), false,
                            RelativeDateTimeFormatter.Style.SHORT));
                    pref.setOrder(showAppsCount++);
                    pref.setOnPreferenceClickListener(preference -> {
                        AppInfoBase.startAppInfoFragment(AppInfoDashboardFragment.class,
                                R.string.application_info_label, pkgName, appEntry.info.uid,
                                mHost, 1001 /*RequestCode*/, getMetricsCategory());
                        return true;
                    });
    
                    if (!rebindPref) {
                        mRecentAppsCategory.addPreference(pref);
                    }
                }
    
                // Remove unused preferences from pref category.
                for (Preference unusedPref : existedAppPreferences.values()) {
                    mRecentAppsCategory.removePreference(unusedPref);
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    进入超父类AppCounter.java

    doInBackground方法进行计算总数量,该计算方法,通过遍历用户和应用双重遍历进行计算,所以算出来的数量double,所以在doInBackground(Void... params)中,只获取第一个用户,在进行遍历应用

    /*
     * Copyright (C) 2016 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
     * except in compliance with the License. You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software distributed under the
     * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     * KIND, either express or implied. See the License for the specific language governing
     * permissions and limitations under the License.
     */
    
    package com.android.settings.applications;
    
    import android.content.Context;
    import android.content.pm.ApplicationInfo;
    import android.content.pm.PackageManager;
    import android.content.pm.UserInfo;
    import android.os.AsyncTask;
    import android.os.UserHandle;
    import android.os.UserManager;
    
    import java.util.List;
    
    public abstract class AppCounter extends AsyncTask<Void, Void, Integer> {
    
        protected final PackageManager mPm;
        protected final UserManager mUm;
    
        public AppCounter(Context context, PackageManager packageManager) {
            mPm = packageManager;
            mUm = (UserManager) context.getSystemService(Context.USER_SERVICE);
        }
    
        @Override
        protected Integer doInBackground(Void... params) {
            //int count = 0;
            //int testNum = 0;
            //int testNum2 = 0;
            //android.util.Log.d("xin.wang", "doInBackground:UserInfo-> " + testNum);
            UserInfo user = mUm.getProfiles(UserHandle.myUserId()).get(0);
            final List<ApplicationInfo> list =
                    mPm.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
                                    | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
                                    | (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0),
                            user.id);
            for (ApplicationInfo info : list) {
                //testNum2 ++;
                //android.util.Log.d("xin.wang", "doInBackground: ApplicationInfo -> " + testNum2);
                if (includeInCount(info)) {
                    count++;
                    //android.util.Log.d("xin.wang", "doInBackground: includeInCount -> " + count);
                }
            }
            /*for (UserInfo user : mUm.getProfiles(UserHandle.myUserId())) {
                testNum ++ ;
                android.util.Log.d("xin.wang", "doInBackground:UserInfo-> " + testNum);
                final List list =
                        mPm.getInstalledApplicationsAsUser(PackageManager.GET_DISABLED_COMPONENTS
                                | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
                                | (user.isAdmin() ? PackageManager.MATCH_ANY_USER : 0),
                                user.id);
                for (ApplicationInfo info : list) {
                    testNum2 ++;
                    android.util.Log.d("xin.wang", "doInBackground: ApplicationInfo -> " + testNum2);
                    if (includeInCount(info)) {
                        count++;
                        android.util.Log.d("xin.wang", "doInBackground: includeInCount -> " + count);
                    }
                }
            }*/
            return count;
        }
    
        @Override
        protected void onPostExecute(Integer count) {
            onCountComplete(count);
        }
    
        void executeInForeground() {
            onPostExecute(doInBackground());
        }
    
        protected abstract void onCountComplete(int num);
        protected abstract boolean includeInCount(ApplicationInfo info);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88

    4.4、进入所有应用

    在这里插入图片描述

    该界面的方法ManageApplications.java类来显示应用图标和名字

    该类有两个内部类适配器

    FilterSpinnerAdapter-> SettingsSpinnerAdapter的适配器

    在这里插入图片描述

    ApplicationsAdapter-> RecyclerView的适配器

    在这里插入图片描述

    createHeader()在该方法中创建头部SpinnerHeader,关闭头部的时候只显示一个FILTER_APPS_PERSONAL的应用

     @VisibleForTesting
        void createHeader() {
            final Activity activity = getActivity();
            final FrameLayout pinnedHeader = mRootView.findViewById(R.id.pinned_header);
            mSpinnerHeader = activity.getLayoutInflater()
                    .inflate(R.layout.manage_apps_filter_spinner, pinnedHeader, false);
            mFilterSpinner = mSpinnerHeader.findViewById(R.id.filter_spinner);
            mFilterAdapter = new FilterSpinnerAdapter(this);
            mFilterSpinner.setAdapter(mFilterAdapter);
            mFilterSpinner.setOnItemSelectedListener(this);
            mFilterSpinner.setVisibility(View.GONE);
            pinnedHeader.addView(mSpinnerHeader, 0);
    
            final AppFilterRegistry appFilterRegistry = AppFilterRegistry.getInstance();
            final int filterType = appFilterRegistry.getDefaultFilterType(mListType);
            android.util.Log.d("xin.wang", "createHeader:-> filterType -> " + mListType + "<- filterType -> " + filterType); //filterType = 4 -> FILTER_APPS_ALL
            android.util.Log.d("xin.wang", "createHeader:->mFilterOptions.size()-> " + mFilterAdapter.getCount());
            //mFilterAdapter.enableFilter(filterType);    //filterType = 4 -> FILTER_APPS_ALL
    
            if (mListType == LIST_TYPE_MAIN) {
                if (UserManager.get(getActivity()).getUserProfiles().size() > 1 && !mIsWorkOnly
                        && !mIsPersonalOnly) {
                    mFilterAdapter.enableFilter(FILTER_APPS_PERSONAL);
                    //mFilterAdapter.enableFilter(FILTER_APPS_WORK);
                }
            }
    
            if (mListType == LIST_TYPE_NOTIFICATION) {
                mFilterAdapter.enableFilter(FILTER_APPS_RECENT);
                mFilterAdapter.enableFilter(FILTER_APPS_FREQUENT);
                mFilterAdapter.enableFilter(FILTER_APPS_BLOCKED);
                mFilterAdapter.enableFilter(FILTER_APPS_ALL);
            }
            if (mListType == LIST_TYPE_HIGH_POWER) {
                mFilterAdapter.enableFilter(FILTER_APPS_POWER_ALLOWLIST_ALL);
            }
    
            setCompositeFilter();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    4.5、开启手机分身后所有应用的显示

    在这里插入图片描述

    个人区域存放所有应用

    工作区域显示双开的应用

    实现的逻辑类ProfileSelectManageApplications
    /*
     * Copyright (C) 2019 The Android Open Source Project
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package com.android.settings.dashboard.profileselector;
    
    import android.os.Bundle;
    
    import androidx.fragment.app.Fragment;
    
    import com.android.settings.applications.manageapplications.ManageApplications;
    
    /**
     * Application Setting page for personal/managed profile.
     */
    public class ProfileSelectManageApplications extends ProfileSelectFragment {
    
        @Override
        public Fragment[] getFragments() {
            final Bundle workOnly = getArguments() != null ? getArguments().deepCopy() : new Bundle();
            workOnly.putInt(EXTRA_PROFILE, ProfileSelectFragment.ProfileType.WORK);
            final Fragment workFragment = new ManageApplications();
            workFragment.setArguments(workOnly);
    
            final Bundle personalOnly = getArguments() != null ? getArguments() : new Bundle();
            personalOnly.putInt(EXTRA_PROFILE, ProfileSelectFragment.ProfileType.PERSONAL);
            final Fragment personalFragment = new ManageApplications();
            personalFragment.setArguments(personalOnly);
            return new Fragment[]{
                    personalFragment, //0
                    workFragment
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    4.6、在Launcher进行设置

    开启应用分身时第一次会创建clone用户,在创建clone用户时会触发该用户的下的app的onPackagesUpdated更新操作,而在无抽屉模式下onPackagesUpdated更新会触发VerifyIdleAppTask任务来更新该用户的图标显示

    在VerifyIdleAppTask任务中对onPackagesUpdated进行判断,如果为clone用户下的应用更新只更新允许应用分身的package包名其他的应用则进行过滤不显示更新。

    路径: Launcher3/src/com/sprd/ext/multimode/VerifyIdleAppTask.java

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    挑战英伟达的新玩家?Tenstorrent虫洞分析
    docker基础学习
    云存储--七牛云--云存储域名绑定--微客外链--微信内置浏览器不支持下载APK(APP)软件的解决方法&&微信跳转浏览器API
    Mac图像编辑器Pixelmator Pro
    NPDP含金量高吗?难考吗?
    替代STM32的GD32,替代KEIL的Eclipse配置---连载4
    VBA筛选排除多个值
    交换两个数字(字符) 学习随笔
    y149.第八章 Servless和Knative从入门到精通 -- Flow(十三)
    大数据在电力行业的应用案例100讲(二十)-数字化场景应用电力计量器具需求分析
  • 原文地址:https://blog.csdn.net/qq_45146250/article/details/126891220