• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.packageinstaller.role.ui;
18 
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.pm.ApplicationInfo;
23 import android.content.pm.PackageManager;
24 import android.os.Bundle;
25 import android.os.UserHandle;
26 import android.provider.Settings;
27 import android.util.ArrayMap;
28 
29 import androidx.annotation.NonNull;
30 import androidx.annotation.Nullable;
31 import androidx.fragment.app.Fragment;
32 import androidx.lifecycle.ViewModelProviders;
33 import androidx.preference.Preference;
34 import androidx.preference.PreferenceCategory;
35 import androidx.preference.PreferenceFragmentCompat;
36 import androidx.preference.PreferenceGroup;
37 import androidx.preference.PreferenceManager;
38 import androidx.preference.PreferenceScreen;
39 
40 import com.android.packageinstaller.permission.utils.Utils;
41 import com.android.packageinstaller.role.model.Role;
42 import com.android.packageinstaller.role.model.Roles;
43 import com.android.permissioncontroller.R;
44 
45 import java.util.List;
46 import java.util.Objects;
47 
48 /**
49  * Child fragment for the list of default apps. Must be added as a child fragment and its parent
50  * fragment must be a {@link PreferenceFragmentCompat} which implements {@link Parent}.
51  *
52  * @param <PF> type of the parent fragment
53  */
54 public class DefaultAppListChildFragment<PF extends PreferenceFragmentCompat
55         & DefaultAppListChildFragment.Parent> extends Fragment
56         implements Preference.OnPreferenceClickListener {
57 
58     private static final String PREFERENCE_KEY_MORE_DEFAULT_APPS =
59             DefaultAppListChildFragment.class.getName() + ".preference.MORE_DEFAULT_APPS";
60 
61     private static final String PREFERENCE_KEY_MANAGE_DOMAIN_URLS =
62             DefaultAppListChildFragment.class.getName() + ".preference.MANAGE_DOMAIN_URLS";
63 
64     private static final String PREFERENCE_KEY_WORK_CATEGORY =
65             DefaultAppListChildFragment.class.getName() + ".preference.WORK_CATEGORY";
66 
67     private DefaultAppListViewModel mViewModel;
68 
69     /**
70      * Create a new instance of this fragment.
71      *
72      * @return a new instance of this fragment
73      */
74     @NonNull
newInstance()75     public static DefaultAppListChildFragment newInstance() {
76         return new DefaultAppListChildFragment();
77     }
78 
79     @Override
onActivityCreated(@ullable Bundle savedInstanceState)80     public void onActivityCreated(@Nullable Bundle savedInstanceState) {
81         super.onActivityCreated(savedInstanceState);
82 
83         mViewModel = ViewModelProviders.of(this).get(DefaultAppListViewModel.class);
84         mViewModel.getLiveData().observe(this, roleItems -> onRoleListChanged());
85         if (mViewModel.hasWorkProfile()) {
86             mViewModel.getWorkLiveData().observe(this, roleItems -> onRoleListChanged());
87         }
88     }
89 
onRoleListChanged()90     private void onRoleListChanged() {
91         List<RoleItem> roleItems = mViewModel.getLiveData().getValue();
92         if (roleItems == null) {
93             return;
94         }
95         boolean hasWorkProfile = mViewModel.hasWorkProfile();
96         List<RoleItem> workRoleItems = null;
97         if (hasWorkProfile) {
98             workRoleItems = mViewModel.getWorkLiveData().getValue();
99             if (workRoleItems == null) {
100                 return;
101             }
102         }
103 
104         PF preferenceFragment = requirePreferenceFragment();
105         PreferenceManager preferenceManager = preferenceFragment.getPreferenceManager();
106         Context context = preferenceManager.getContext();
107         PreferenceScreen preferenceScreen = preferenceFragment.getPreferenceScreen();
108         ArrayMap<String, Preference> oldPreferences = new ArrayMap<>();
109         PreferenceCategory oldWorkPreferenceCategory = null;
110         ArrayMap<String, Preference> oldWorkPreferences = new ArrayMap<>();
111         if (preferenceScreen == null) {
112             preferenceScreen = preferenceManager.createPreferenceScreen(context);
113             preferenceFragment.setPreferenceScreen(preferenceScreen);
114         } else {
115             oldWorkPreferenceCategory = preferenceScreen.findPreference(
116                     PREFERENCE_KEY_WORK_CATEGORY);
117             if (oldWorkPreferenceCategory != null) {
118                 clearPreferences(oldWorkPreferenceCategory, oldWorkPreferences);
119                 preferenceScreen.removePreference(oldWorkPreferenceCategory);
120                 oldWorkPreferenceCategory.setOrder(Preference.DEFAULT_ORDER);
121             }
122             clearPreferences(preferenceScreen, oldPreferences);
123         }
124 
125         addPreferences(preferenceScreen, roleItems, oldPreferences, this, mViewModel.getUser(),
126                 context);
127         addMoreDefaultAppsPreference(preferenceScreen, oldPreferences, context);
128         addManageDomainUrlsPreference(preferenceScreen, oldPreferences, context);
129         if (hasWorkProfile && !workRoleItems.isEmpty()) {
130             PreferenceCategory workPreferenceCategory = oldWorkPreferenceCategory;
131             if (workPreferenceCategory == null) {
132                 workPreferenceCategory = new PreferenceCategory(context);
133                 workPreferenceCategory.setKey(PREFERENCE_KEY_WORK_CATEGORY);
134                 workPreferenceCategory.setTitle(R.string.default_apps_for_work);
135             }
136             preferenceScreen.addPreference(workPreferenceCategory);
137             addPreferences(workPreferenceCategory, workRoleItems, oldWorkPreferences, this,
138                     mViewModel.getWorkProfile(), context);
139         }
140 
141         preferenceFragment.onPreferenceScreenChanged();
142     }
143 
clearPreferences(@onNull PreferenceGroup preferenceGroup, @NonNull ArrayMap<String, Preference> oldPreferences)144     private static void clearPreferences(@NonNull PreferenceGroup preferenceGroup,
145             @NonNull ArrayMap<String, Preference> oldPreferences) {
146         for (int i = preferenceGroup.getPreferenceCount() - 1; i >= 0; --i) {
147             Preference preference = preferenceGroup.getPreference(i);
148 
149             preferenceGroup.removePreference(preference);
150             preference.setOrder(Preference.DEFAULT_ORDER);
151             oldPreferences.put(preference.getKey(), preference);
152         }
153     }
154 
addPreferences(@onNull PreferenceGroup preferenceGroup, @NonNull List<RoleItem> roleItems, @NonNull ArrayMap<String, Preference> oldPreferences, @NonNull Preference.OnPreferenceClickListener listener, @NonNull UserHandle user, @NonNull Context context)155     private void addPreferences(@NonNull PreferenceGroup preferenceGroup,
156             @NonNull List<RoleItem> roleItems, @NonNull ArrayMap<String, Preference> oldPreferences,
157             @NonNull Preference.OnPreferenceClickListener listener, @NonNull UserHandle user,
158             @NonNull Context context) {
159         PF preferenceFragment = requirePreferenceFragment();
160         int roleItemsSize = roleItems.size();
161         for (int i = 0; i < roleItemsSize; i++) {
162             RoleItem roleItem = roleItems.get(i);
163 
164             Role role = roleItem.getRole();
165             TwoTargetPreference preference = (TwoTargetPreference) oldPreferences.get(
166                     role.getName());
167             if (preference == null) {
168                 preference = preferenceFragment.createPreference(context);
169                 preference.setKey(role.getName());
170                 preference.setIconSpaceReserved(true);
171                 preference.setTitle(role.getShortLabelResource());
172                 preference.setPersistent(false);
173                 preference.setOnPreferenceClickListener(listener);
174                 preference.getExtras().putParcelable(Intent.EXTRA_USER, user);
175             }
176 
177             List<ApplicationInfo> holderApplicationInfos = roleItem.getHolderApplicationInfos();
178             if (holderApplicationInfos.isEmpty()) {
179                 preference.setIcon(null);
180                 preference.setSummary(R.string.default_app_none);
181             } else {
182                 ApplicationInfo holderApplicationInfo = holderApplicationInfos.get(0);
183                 preference.setIcon(Utils.getBadgedIcon(context, holderApplicationInfo));
184                 preference.setSummary(Utils.getAppLabel(holderApplicationInfo, context));
185             }
186             role.preparePreferenceAsUser(preference, user, context);
187 
188             preferenceGroup.addPreference(preference);
189         }
190     }
191 
192     @Override
onPreferenceClick(@onNull Preference preference)193     public boolean onPreferenceClick(@NonNull Preference preference) {
194         String roleName = preference.getKey();
195         Context context = requireContext();
196         Role role = Roles.get(context).get(roleName);
197         UserHandle user = preference.getExtras().getParcelable(Intent.EXTRA_USER);
198         Intent intent = role.getManageIntentAsUser(user, context);
199         if (intent == null) {
200             intent = DefaultAppActivity.createIntent(roleName, user, context);
201         }
202         startActivity(intent);
203         return true;
204     }
205 
addMoreDefaultAppsPreference(@onNull PreferenceGroup preferenceGroup, @NonNull ArrayMap<String, Preference> oldPreferences, @NonNull Context context)206     private static void addMoreDefaultAppsPreference(@NonNull PreferenceGroup preferenceGroup,
207             @NonNull ArrayMap<String, Preference> oldPreferences, @NonNull Context context) {
208         Intent intent = new Intent(Settings.ACTION_MANAGE_MORE_DEFAULT_APPS_SETTINGS);
209         if (!isIntentResolvedToSettings(intent, context)) {
210             return;
211         }
212 
213         Preference preference = oldPreferences.get(PREFERENCE_KEY_MORE_DEFAULT_APPS);
214         if (preference == null) {
215             preference = new Preference(context);
216             preference.setKey(PREFERENCE_KEY_MORE_DEFAULT_APPS);
217             preference.setIconSpaceReserved(true);
218             preference.setTitle(context.getString(R.string.default_apps_more));
219             preference.setPersistent(false);
220             preference.setOnPreferenceClickListener(preference2 -> {
221                 context.startActivity(intent);
222                 return true;
223             });
224         }
225 
226         preferenceGroup.addPreference(preference);
227     }
228 
addManageDomainUrlsPreference(@onNull PreferenceGroup preferenceGroup, @NonNull ArrayMap<String, Preference> oldPreferences, @NonNull Context context)229     private static void addManageDomainUrlsPreference(@NonNull PreferenceGroup preferenceGroup,
230             @NonNull ArrayMap<String, Preference> oldPreferences, @NonNull Context context) {
231         Intent intent = new Intent(Settings.ACTION_MANAGE_DOMAIN_URLS);
232         if (!isIntentResolvedToSettings(intent, context)) {
233             return;
234         }
235 
236         Preference preference = oldPreferences.get(PREFERENCE_KEY_MANAGE_DOMAIN_URLS);
237         if (preference == null) {
238             preference = new Preference(context);
239             preference.setKey(PREFERENCE_KEY_MANAGE_DOMAIN_URLS);
240             preference.setIconSpaceReserved(true);
241             preference.setTitle(context.getString(R.string.default_apps_manage_domain_urls));
242             preference.setPersistent(false);
243             preference.setOnPreferenceClickListener(preference2 -> {
244                 context.startActivity(intent);
245                 return true;
246             });
247         }
248 
249         preferenceGroup.addPreference(preference);
250     }
251 
isIntentResolvedToSettings(@onNull Intent intent, @NonNull Context context)252     private static boolean isIntentResolvedToSettings(@NonNull Intent intent,
253             @NonNull Context context) {
254         PackageManager packageManager = context.getPackageManager();
255         ComponentName componentName = intent.resolveActivity(packageManager);
256         if (componentName == null) {
257             return false;
258         }
259         Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS);
260         String settingsPackageName = settingsIntent.resolveActivity(packageManager)
261                 .getPackageName();
262         return Objects.equals(componentName.getPackageName(), settingsPackageName);
263     }
264 
265     @NonNull
requirePreferenceFragment()266     private PF requirePreferenceFragment() {
267         //noinspection unchecked
268         return (PF) requireParentFragment();
269     }
270 
271     /**
272      * Interface that the parent fragment must implement.
273      */
274     public interface Parent {
275 
276         /**
277          * Create a new preference for a default app.
278          *
279          * @param context the {@code Context} to use when creating the preference.
280          *
281          * @return a new preference for a default app
282          */
283         @NonNull
createPreference(@onNull Context context)284         TwoTargetPreference createPreference(@NonNull Context context);
285 
286         /**
287          * Callback when changes have been made to the {@link PreferenceScreen} of the parent
288          * {@link PreferenceFragmentCompat}.
289          */
onPreferenceScreenChanged()290         void onPreferenceScreenChanged();
291     }
292 }
293