1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.applications;
18 
19 import static com.android.settings.Utils.PROPERTY_CLONED_APPS_ENABLED;
20 
21 import android.content.Context;
22 import android.os.AsyncTask;
23 import android.os.Bundle;
24 import android.os.UserHandle;
25 import android.provider.DeviceConfig;
26 
27 import androidx.lifecycle.Lifecycle;
28 import androidx.lifecycle.LifecycleObserver;
29 import androidx.lifecycle.OnLifecycleEvent;
30 import androidx.preference.Preference;
31 import androidx.preference.PreferenceScreen;
32 
33 import com.android.settings.R;
34 import com.android.settings.Utils;
35 import com.android.settings.core.BasePreferenceController;
36 import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
37 
38 import java.util.Arrays;
39 import java.util.List;
40 
41 /**
42  * A preference controller handling the logic for updating the summary of cloned apps.
43  */
44 public class ClonedAppsPreferenceController extends BasePreferenceController
45         implements LifecycleObserver {
46     private Preference mPreference;
47     private Context mContext;
48 
ClonedAppsPreferenceController(Context context, String preferenceKey)49     public ClonedAppsPreferenceController(Context context, String preferenceKey) {
50         super(context, preferenceKey);
51         mContext = context;
52     }
53 
54     @Override
getAvailabilityStatus()55     public int getAvailabilityStatus() {
56         return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_APP_CLONING,
57                 PROPERTY_CLONED_APPS_ENABLED, true)
58                 && mContext.getResources().getBoolean(R.bool.config_cloned_apps_page_enabled)
59                 ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
60     }
61 
62     @Override
displayPreference(PreferenceScreen screen)63     public void displayPreference(PreferenceScreen screen) {
64         super.displayPreference(screen);
65         mPreference = screen.findPreference(getPreferenceKey());
66     }
67     /**
68      * On lifecycle resume event.
69      */
70     @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
onResume()71     public void onResume() {
72         updatePreferenceSummary();
73     }
74 
updatePreferenceSummary()75     private void updatePreferenceSummary() {
76         if (!isAvailable()) {
77             return;
78         }
79         new AsyncTask<Void, Void, Integer[]>() {
80 
81             @Override
82             protected Integer[] doInBackground(Void... unused) {
83                 // Get list of allowlisted cloneable apps.
84                 List<String> cloneableApps = Arrays.asList(
85                         mContext.getResources().getStringArray(
86                                 com.android.internal.R.array.cloneable_apps));
87                 List<String> primaryUserApps = mContext.getPackageManager()
88                         .getInstalledPackagesAsUser(/* flags*/ 0, UserHandle.myUserId()).stream()
89                         .map(x -> x.packageName).toList();
90                 // Count number of installed apps in system user.
91                 int availableAppsCount = (int) cloneableApps.stream()
92                         .filter(x -> primaryUserApps.contains(x)).count();
93 
94                 int cloneUserId = Utils.getCloneUserId(mContext);
95                 if (cloneUserId == -1) {
96                     return new Integer[]{0, availableAppsCount};
97                 }
98                 // Get all apps in clone profile if present.
99                 List<String> cloneProfileApps = mContext.getPackageManager()
100                         .getInstalledPackagesAsUser(/* flags*/ 0, cloneUserId).stream()
101                         .map(x -> x.packageName).toList();
102                 // Count number of allowlisted app present in clone profile.
103                 int clonedAppsCount = (int) cloneableApps.stream()
104                         .filter(x -> cloneProfileApps.contains(x)).count();
105 
106                 return new Integer[]{clonedAppsCount, availableAppsCount - clonedAppsCount};
107             }
108 
109             @Override
110             protected void onPostExecute(Integer[] countInfo) {
111                 updateSummary(countInfo[0], countInfo[1]);
112             }
113         }.execute();
114     }
115 
updateSummary(int clonedAppsCount, int availableAppsCount)116     private void updateSummary(int clonedAppsCount, int availableAppsCount) {
117         mPreference.setSummary(mContext.getResources().getString(
118                 R.string.cloned_apps_summary, clonedAppsCount, availableAppsCount));
119     }
120 
121     @Override
handlePreferenceTreeClick(Preference preference)122     public boolean handlePreferenceTreeClick(Preference preference) {
123         // Add this extra so that work tab is not displayed on Cloned Apps page.
124         if (getPreferenceKey().equals(preference.getKey())) {
125             final Bundle extras = preference.getExtras();
126             extras.putInt(ProfileSelectFragment.EXTRA_PROFILE,
127                     ProfileSelectFragment.ProfileType.PERSONAL);
128         }
129 
130         return super.handlePreferenceTreeClick(preference);
131     }
132 }
133