1 /* 2 * Copyright (C) 2020 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 package com.android.settings.applications.specialaccess.interactacrossprofiles; 17 18 import static android.content.pm.PackageManager.GET_ACTIVITIES; 19 20 import android.annotation.Nullable; 21 import android.app.settings.SettingsEnums; 22 import android.content.Context; 23 import android.content.pm.ApplicationInfo; 24 import android.content.pm.CrossProfileApps; 25 import android.content.pm.PackageInfo; 26 import android.content.pm.PackageManager; 27 import android.content.pm.UserInfo; 28 import android.os.Bundle; 29 import android.os.UserHandle; 30 import android.os.UserManager; 31 import android.util.IconDrawableFactory; 32 import android.util.Pair; 33 import android.view.View; 34 35 import androidx.preference.Preference; 36 import androidx.preference.Preference.OnPreferenceClickListener; 37 import androidx.preference.PreferenceScreen; 38 39 import com.android.settings.R; 40 import com.android.settings.applications.AppInfoBase; 41 import com.android.settings.search.BaseSearchIndexProvider; 42 import com.android.settings.widget.EmptyTextSettings; 43 import com.android.settingslib.search.SearchIndexable; 44 import com.android.settingslib.widget.AppPreference; 45 46 import java.util.ArrayList; 47 import java.util.List; 48 49 @SearchIndexable 50 public class InteractAcrossProfilesSettings extends EmptyTextSettings { 51 private Context mContext; 52 private PackageManager mPackageManager; 53 private UserManager mUserManager; 54 private CrossProfileApps mCrossProfileApps; 55 private IconDrawableFactory mIconDrawableFactory; 56 57 @Override onCreate(Bundle icicle)58 public void onCreate(Bundle icicle) { 59 super.onCreate(icicle); 60 61 mContext = getContext(); 62 mPackageManager = mContext.getPackageManager(); 63 mUserManager = mContext.getSystemService(UserManager.class); 64 mIconDrawableFactory = IconDrawableFactory.newInstance(mContext); 65 mCrossProfileApps = mContext.getSystemService(CrossProfileApps.class); 66 } 67 68 @Override onResume()69 public void onResume() { 70 super.onResume(); 71 72 final PreferenceScreen screen = getPreferenceScreen(); 73 screen.removeAll(); 74 75 final ArrayList<Pair<ApplicationInfo, UserHandle>> crossProfileApps = 76 collectConfigurableApps(mPackageManager, mUserManager, mCrossProfileApps); 77 78 final Context prefContext = getPrefContext(); 79 for (final Pair<ApplicationInfo, UserHandle> appData : crossProfileApps) { 80 final ApplicationInfo appInfo = appData.first; 81 final UserHandle user = appData.second; 82 final String packageName = appInfo.packageName; 83 final CharSequence label = appInfo.loadLabel(mPackageManager); 84 85 final Preference pref = new AppPreference(prefContext); 86 pref.setIcon(mIconDrawableFactory.getBadgedIcon(appInfo, user.getIdentifier())); 87 pref.setTitle(mPackageManager.getUserBadgedLabel(label, user)); 88 pref.setSummary(InteractAcrossProfilesDetails.getPreferenceSummary( 89 prefContext, packageName)); 90 pref.setOnPreferenceClickListener(new OnPreferenceClickListener() { 91 @Override 92 public boolean onPreferenceClick(Preference preference) { 93 AppInfoBase.startAppInfoFragment(InteractAcrossProfilesDetails.class, 94 R.string.interact_across_profiles_title, 95 packageName, 96 appInfo.uid, 97 InteractAcrossProfilesSettings.this/* source */, 98 -1/* request */, 99 getMetricsCategory()); 100 return true; 101 } 102 }); 103 screen.addPreference(pref); 104 } 105 } 106 107 @Override onViewCreated(View view, @Nullable Bundle savedInstanceState)108 public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { 109 super.onViewCreated(view, savedInstanceState); 110 setEmptyText(R.string.interact_across_profiles_empty_text); 111 } 112 113 @Override getPreferenceScreenResId()114 protected int getPreferenceScreenResId() { 115 return R.xml.interact_across_profiles; 116 } 117 118 @Override getMetricsCategory()119 public int getMetricsCategory() { 120 return SettingsEnums.INTERACT_ACROSS_PROFILES; 121 } 122 123 /** 124 * @return the list of applications for the personal profile in the calling user's profile group 125 * that can configure interact across profiles. 126 */ collectConfigurableApps( PackageManager packageManager, UserManager userManager, CrossProfileApps crossProfileApps)127 static ArrayList<Pair<ApplicationInfo, UserHandle>> collectConfigurableApps( 128 PackageManager packageManager, UserManager userManager, 129 CrossProfileApps crossProfileApps) { 130 final UserHandle workProfile = getWorkProfile(userManager); 131 if (workProfile == null) { 132 return new ArrayList<>(); 133 } 134 final UserHandle personalProfile = userManager.getProfileParent(workProfile); 135 if (personalProfile == null) { 136 return new ArrayList<>(); 137 } 138 139 final ArrayList<Pair<ApplicationInfo, UserHandle>> apps = new ArrayList<>(); 140 for (PackageInfo packageInfo : getAllInstalledPackages( 141 packageManager, personalProfile, workProfile)) { 142 if (crossProfileApps.canUserAttemptToConfigureInteractAcrossProfiles( 143 packageInfo.packageName)) { 144 apps.add(new Pair<>(packageInfo.applicationInfo, personalProfile)); 145 } 146 } 147 return apps; 148 } 149 getAllInstalledPackages( PackageManager packageManager, UserHandle personalProfile, UserHandle workProfile)150 private static List<PackageInfo> getAllInstalledPackages( 151 PackageManager packageManager, UserHandle personalProfile, UserHandle workProfile) { 152 List<PackageInfo> personalPackages = packageManager.getInstalledPackagesAsUser( 153 GET_ACTIVITIES, personalProfile.getIdentifier()); 154 List<PackageInfo> workPackages = packageManager.getInstalledPackagesAsUser( 155 GET_ACTIVITIES, workProfile.getIdentifier()); 156 List<PackageInfo> allPackages = new ArrayList<>(personalPackages); 157 for (PackageInfo workPackage : workPackages) { 158 if (allPackages.stream().noneMatch( 159 p -> workPackage.packageName.equals(p.packageName))) { 160 allPackages.add(workPackage); 161 } 162 } 163 return allPackages; 164 } 165 166 /** 167 * @return the number of applications that can interact across profiles. 168 */ getNumberOfEnabledApps( Context context, PackageManager packageManager, UserManager userManager, CrossProfileApps crossProfileApps)169 static int getNumberOfEnabledApps( 170 Context context, PackageManager packageManager, UserManager userManager, 171 CrossProfileApps crossProfileApps) { 172 UserHandle workProfile = getWorkProfile(userManager); 173 if (workProfile == null) { 174 return 0; 175 } 176 UserHandle personalProfile = userManager.getProfileParent(workProfile); 177 if (personalProfile == null) { 178 return 0; 179 } 180 final ArrayList<Pair<ApplicationInfo, UserHandle>> apps = 181 collectConfigurableApps(packageManager, userManager, crossProfileApps); 182 apps.removeIf( 183 app -> !InteractAcrossProfilesDetails.isInteractAcrossProfilesEnabled( 184 context, app.first.packageName) 185 || !crossProfileApps.canConfigureInteractAcrossProfiles( 186 app.first.packageName)); 187 return apps.size(); 188 } 189 190 /** 191 * Returns the work profile in the profile group of the calling user. 192 * Returns null if not found. 193 */ 194 @Nullable getWorkProfile(UserManager userManager)195 static UserHandle getWorkProfile(UserManager userManager) { 196 for (UserInfo user : userManager.getProfiles(UserHandle.myUserId())) { 197 if (userManager.isManagedProfile(user.id)) { 198 return user.getUserHandle(); 199 } 200 } 201 return null; 202 } 203 204 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 205 new BaseSearchIndexProvider(R.xml.interact_across_profiles); 206 } 207