1 /* 2 * Copyright (C) 2019 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.permissioncontroller.permission.ui.auto; 18 19 20 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; 21 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; 22 23 import android.app.Application; 24 import android.content.Context; 25 import android.os.Bundle; 26 27 import androidx.annotation.Nullable; 28 import androidx.fragment.app.FragmentTransaction; 29 import androidx.lifecycle.ViewModelProvider; 30 import androidx.preference.Preference; 31 import androidx.preference.PreferenceScreen; 32 33 import com.android.permissioncontroller.R; 34 import com.android.permissioncontroller.auto.AutoSettingsFrameFragment; 35 import com.android.permissioncontroller.permission.model.livedatatypes.PermGroupPackagesUiInfo; 36 import com.android.permissioncontroller.permission.ui.UnusedAppsFragment; 37 import com.android.permissioncontroller.permission.ui.model.ManagePermissionsViewModel; 38 import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel; 39 import com.android.permissioncontroller.permission.ui.model.PermissionGroupPreferenceUtils; 40 import com.android.permissioncontroller.permission.utils.Utils; 41 42 import java.util.List; 43 44 /** 45 * Shows the standard permissions that can be granted/denied. 46 * 47 * Largely based on the implementation of 48 * {@link com.android.permissioncontroller.permission.ui.television.ManagePermissionsFragment}. 49 */ 50 public class AutoManageStandardPermissionsFragment extends AutoSettingsFrameFragment { 51 52 private static final String KEY_OTHER_PERMISSIONS = "other_permissions"; 53 private static final String KEY_AUTO_REVOKE = "auto_revoke_key"; 54 private ManagePermissionsViewModel mManagePermissionsViewModel; 55 private ManageStandardPermissionsViewModel mManageStandardPermissionsViewModel; 56 57 /** Returns a new instance of {@link AutoManageStandardPermissionsFragment}. */ newInstance()58 public static AutoManageStandardPermissionsFragment newInstance() { 59 return new AutoManageStandardPermissionsFragment(); 60 } 61 62 @Override onCreate(@ullable Bundle savedInstanceState)63 public void onCreate(@Nullable Bundle savedInstanceState) { 64 super.onCreate(savedInstanceState); 65 setLoading(true); 66 setHeaderLabel(getString(R.string.app_permission_manager)); 67 68 final Application application = getActivity().getApplication(); 69 final ViewModelProvider.Factory viewModelFactory = 70 ViewModelProvider.AndroidViewModelFactory.getInstance(application); 71 mManagePermissionsViewModel = new ViewModelProvider(this, viewModelFactory) 72 .get(ManagePermissionsViewModel.class); 73 mManageStandardPermissionsViewModel = new ViewModelProvider(this, 74 ViewModelProvider.AndroidViewModelFactory.getInstance(application)) 75 .get(ManageStandardPermissionsViewModel.class); 76 mManagePermissionsViewModel.getUsedPermissionGroups().observe(this, 77 this::onPermissionGroupsChanged); 78 mManageStandardPermissionsViewModel.getNumAutoRevoked().observe(this, 79 this::onNumAutoRevokedChanged); 80 } 81 82 @Override onCreatePreferences(Bundle bundle, String s)83 public void onCreatePreferences(Bundle bundle, String s) { 84 setPreferenceScreen(getPreferenceManager().createPreferenceScreen(getContext())); 85 } 86 onPermissionGroupsChanged(List<PermGroupPackagesUiInfo> permissionGroups)87 private void onPermissionGroupsChanged(List<PermGroupPackagesUiInfo> permissionGroups) { 88 final Context context = getPreferenceManager().getContext(); 89 if (context == null) { 90 return; 91 } 92 93 final PreferenceScreen screen = getPreferenceScreen(); 94 95 // First check if "Other preferences" button exists. If it does: remove it, but hold on to 96 // the reference - we'll it add back later, after the preferences for the permission groups 97 // have been updated. If it does not exist: create and hold on to it. 98 Preference otherPermissionsPreference = screen.findPreference(KEY_OTHER_PERMISSIONS); 99 if (otherPermissionsPreference == null) { 100 otherPermissionsPreference = buildOtherPermissionsPreference(context); 101 } else { 102 screen.removePreference(otherPermissionsPreference); 103 // The PreferenceScreen is ordering items as added 104 // (see PreferenceGroup#setOrderingAsAdded()), which means that it assigns positional 105 // indexes ("order") to Preferences incrementally as they are added, BUT ONLY IF their 106 // current "order" is the DEFAULT_ORDER. 107 // However, when the Preferences are removed from the group they keep their "order" and 108 // thus when they are re-added to a group (same or another) their "order" does not get 109 // re-assigned, so they may show up at the position they previously were at. 110 // We want the otherPermissionsPreference to always be the last in the list, so reset 111 // its "order" to DEFAULT, so that we add last to the group, it indeed goes into the 112 // last position. 113 otherPermissionsPreference.setOrder(Preference.DEFAULT_ORDER); 114 } 115 116 PermissionGroupPreferenceUtils.updateGroupOfPermissionPreferences(context, screen, 117 permissionGroups); 118 119 screen.addPreference(otherPermissionsPreference); 120 121 // load initial auto-revoke count, if it is ready 122 Integer numAutoRevoked = mManageStandardPermissionsViewModel.getNumAutoRevoked().getValue(); 123 onNumAutoRevokedChanged(numAutoRevoked); 124 125 setLoading(false); 126 } 127 128 buildOtherPermissionsPreference(Context context)129 private Preference buildOtherPermissionsPreference(Context context) { 130 final Preference preference = new Preference(context); 131 preference.setPersistent(false); 132 preference.setKey(KEY_OTHER_PERMISSIONS); 133 preference.setTitle(R.string.other_permissions_label); 134 preference.setIcon( 135 Utils.applyTint( 136 context, R.drawable.ic_more_items, android.R.attr.colorControlNormal)); 137 preference.setOnPreferenceClickListener(p -> { 138 AutoManageOtherPermissionsFragment frag = 139 new AutoManageOtherPermissionsFragment(); 140 frag.setTargetFragment(AutoManageStandardPermissionsFragment.this, 141 /* requestCode= */ 0); 142 FragmentTransaction ft = getFragmentManager().beginTransaction(); 143 ft.replace(android.R.id.content, frag); 144 ft.addToBackStack(null); 145 ft.commit(); 146 return true; 147 }); 148 // Make invisible for now and subscribe to the LiveData that tracks whether there are any 149 // unused or additional permissions. 150 preference.setVisible(false); 151 mManagePermissionsViewModel.hasUnusedOrAdditionalPermissionGroups().observe(this, 152 preference::setVisible); 153 return preference; 154 } 155 onNumAutoRevokedChanged(Integer numAutoRevoked)156 private void onNumAutoRevokedChanged(Integer numAutoRevoked) { 157 // to prevent ui jank, don't display auto-revoke until categories have loaded 158 if (mManagePermissionsViewModel.getUsedPermissionGroups().getValue() == null) { 159 return; 160 } 161 Preference autoRevokePreference = getPreferenceScreen().findPreference(KEY_AUTO_REVOKE); 162 if (numAutoRevoked != null && numAutoRevoked != 0) { 163 if (autoRevokePreference == null) { 164 autoRevokePreference = new Preference(getPreferenceManager().getContext()); 165 autoRevokePreference.setOrder(-1); 166 autoRevokePreference.setKey(KEY_AUTO_REVOKE); 167 autoRevokePreference.setSingleLineTitle(false); 168 autoRevokePreference.setIcon(R.drawable.ic_info_outline); 169 autoRevokePreference.setTitle( 170 R.string.auto_revoke_permission_notification_title); 171 autoRevokePreference.setSummary( 172 R.string.auto_revoke_setting_subtitle); 173 autoRevokePreference.setOnPreferenceClickListener(preference -> { 174 AutoUnusedAppsFragment frag = AutoUnusedAppsFragment.newInstance(); 175 frag.setArguments(UnusedAppsFragment.createArgs( 176 getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID))); 177 frag.setTargetFragment(AutoManageStandardPermissionsFragment.this, 178 /* requestCode= */ 0); 179 FragmentTransaction ft = getFragmentManager().beginTransaction(); 180 ft.replace(android.R.id.content, frag); 181 ft.addToBackStack(null); 182 ft.commit(); 183 return true; 184 }); 185 186 getPreferenceScreen().addPreference(autoRevokePreference); 187 } 188 } else if (numAutoRevoked != null && autoRevokePreference != null) { 189 getPreferenceScreen().removePreference(autoRevokePreference); 190 } 191 } 192 } 193