1 /* 2 * Copyright (C) 2016 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.permissioncontroller.permission.ui.handheld; 17 18 import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory; 19 20 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; 21 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; 22 import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack; 23 24 import android.app.Application; 25 import android.os.Bundle; 26 import android.util.Log; 27 import android.view.MenuItem; 28 29 import androidx.lifecycle.ViewModelProvider; 30 import androidx.preference.Preference; 31 import androidx.preference.PreferenceScreen; 32 33 import com.android.modules.utils.build.SdkLevel; 34 import com.android.permission.flags.Flags; 35 import com.android.permissioncontroller.R; 36 import com.android.permissioncontroller.permission.data.PermGroupsPackagesUiInfoLiveData; 37 import com.android.permissioncontroller.permission.ui.UnusedAppsFragment; 38 import com.android.permissioncontroller.permission.ui.model.ManageStandardPermissionsViewModel; 39 import com.android.permissioncontroller.permission.utils.StringUtils; 40 import com.android.permissioncontroller.permission.utils.Utils; 41 42 /** 43 * Fragment that allows the user to manage standard permissions. 44 */ 45 public final class ManageStandardPermissionsFragment extends ManagePermissionsFragment { 46 private static final String EXTRA_PREFS_KEY = "extra_prefs_key"; 47 private static final String AUTO_REVOKE_KEY = "auto_revoke_key"; 48 private static final String LOG_TAG = ManageStandardPermissionsFragment.class.getSimpleName(); 49 private ManageStandardPermissionsViewModel mViewModel; 50 51 /** 52 * Create a bundle with the arguments needed by this fragment 53 * 54 * @param sessionId The current session ID 55 * @return A bundle with all of the args placed 56 */ createArgs(long sessionId)57 public static Bundle createArgs(long sessionId) { 58 Bundle arguments = new Bundle(); 59 arguments.putLong(EXTRA_SESSION_ID, sessionId); 60 return arguments; 61 } 62 getPermGroupsLiveData()63 private PermGroupsPackagesUiInfoLiveData getPermGroupsLiveData() { 64 if (Flags.declutteredPermissionManagerEnabled()) { 65 return mViewModel.getUsedStandardPermGroupsUiInfo(); 66 } else { 67 return mViewModel.getUiDataLiveData(); 68 } 69 } 70 71 @Override onCreate(Bundle icicle)72 public void onCreate(Bundle icicle) { 73 super.onCreate(icicle); 74 75 final Application application = getActivity().getApplication(); 76 mViewModel = new ViewModelProvider(this, AndroidViewModelFactory.getInstance(application)) 77 .get(ManageStandardPermissionsViewModel.class); 78 mPermissionGroups = getPermGroupsLiveData().getValue(); 79 80 getPermGroupsLiveData().observe(this, permissionGroups -> { 81 // Once we have loaded data for the first time, further loads should be staggered, 82 // for performance reasons. 83 getPermGroupsLiveData().setLoadStaggered(true); 84 if (permissionGroups != null) { 85 mPermissionGroups = permissionGroups; 86 updatePermissionsUi(); 87 } else { 88 Log.e(LOG_TAG, "ViewModel returned null data, exiting"); 89 getActivity().finishAfterTransition(); 90 } 91 92 // If we've loaded all LiveDatas, no need to prioritize loading any particular one 93 if (!getPermGroupsLiveData().isStale()) { 94 getPermGroupsLiveData().setFirstLoadGroup(null); 95 } 96 }); 97 98 mViewModel.getNumCustomPermGroups().observe(this, permNames -> updatePermissionsUi()); 99 mViewModel.getNumAutoRevoked().observe(this, show -> updatePermissionsUi()); 100 if (Flags.declutteredPermissionManagerEnabled()) { 101 mViewModel.getNumUnusedStandardPermGroups().observe( 102 this, show -> updatePermissionsUi() 103 ); 104 } 105 } 106 107 @Override onStart()108 public void onStart() { 109 super.onStart(); 110 111 getActivity().setTitle(com.android.permissioncontroller.R.string.app_permission_manager); 112 } 113 114 @Override onOptionsItemSelected(MenuItem item)115 public boolean onOptionsItemSelected(MenuItem item) { 116 switch (item.getItemId()) { 117 case android.R.id.home: 118 pressBack(this); 119 return true; 120 } 121 return super.onOptionsItemSelected(item); 122 } 123 124 @Override updatePermissionsUi()125 protected PreferenceScreen updatePermissionsUi() { 126 PreferenceScreen screen = super.updatePermissionsUi(); 127 if (screen == null) { 128 return null; 129 } 130 131 // Check if we need an additional permissions preference 132 int numExtraPermissions = 0; 133 if (mViewModel.getNumCustomPermGroups().getValue() != null) { 134 numExtraPermissions = mViewModel.getNumCustomPermGroups().getValue(); 135 } 136 if (Flags.declutteredPermissionManagerEnabled()) { 137 if (mViewModel.getNumUnusedStandardPermGroups().getValue() != null) { 138 // When decluttered permission manager is enabled, unused 139 // permission groups will also be displayed in the additional 140 // permissions screen. 141 numExtraPermissions += mViewModel.getNumUnusedStandardPermGroups().getValue(); 142 } 143 } 144 145 Preference additionalPermissionsPreference = screen.findPreference(EXTRA_PREFS_KEY); 146 if (numExtraPermissions == 0) { 147 if (additionalPermissionsPreference != null) { 148 screen.removePreference(additionalPermissionsPreference); 149 } 150 } else { 151 if (additionalPermissionsPreference == null) { 152 additionalPermissionsPreference = new FixedSizeIconPreference( 153 getPreferenceManager().getContext()); 154 additionalPermissionsPreference.setKey(EXTRA_PREFS_KEY); 155 additionalPermissionsPreference.setIcon(Utils.applyTint(getActivity(), 156 R.drawable.ic_more_items, 157 android.R.attr.colorControlNormal)); 158 additionalPermissionsPreference.setTitle(R.string.additional_permissions); 159 additionalPermissionsPreference.setOnPreferenceClickListener(preference -> { 160 mViewModel.showCustomPermissions(this, 161 ManageCustomPermissionsFragment.createArgs( 162 getArguments().getLong(EXTRA_SESSION_ID))); 163 return true; 164 }); 165 166 screen.addPreference(additionalPermissionsPreference); 167 } 168 169 additionalPermissionsPreference.setSummary(StringUtils.getIcuPluralsString(getContext(), 170 R.string.additional_permissions_more, numExtraPermissions)); 171 } 172 173 Integer numAutoRevoked = mViewModel.getNumAutoRevoked().getValue(); 174 175 Preference autoRevokePreference = screen.findPreference(AUTO_REVOKE_KEY); 176 if (numAutoRevoked != null && numAutoRevoked != 0) { 177 if (autoRevokePreference == null) { 178 if (SdkLevel.isAtLeastS()) { 179 autoRevokePreference = createAutoRevokeFooterPreferenceForSPlus(); 180 } else { 181 autoRevokePreference = createAutoRevokeFooterPreferenceForR(); 182 } 183 autoRevokePreference.setKey(AUTO_REVOKE_KEY); 184 screen.addPreference(autoRevokePreference); 185 } 186 } else if (numAutoRevoked != null && autoRevokePreference != null) { 187 screen.removePreference(autoRevokePreference); 188 } 189 190 return screen; 191 } 192 createAutoRevokeFooterPreferenceForSPlus()193 private PermissionFooterPreference createAutoRevokeFooterPreferenceForSPlus() { 194 PermissionFooterPreference autoRevokePreference = 195 new PermissionFooterPreference(getContext()); 196 autoRevokePreference.setSummary(R.string.auto_revoked_apps_page_summary); 197 autoRevokePreference.setLearnMoreAction(view -> { 198 mViewModel.showAutoRevoke(this, UnusedAppsFragment.createArgs( 199 getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID))); 200 }); 201 return autoRevokePreference; 202 } 203 createAutoRevokeFooterPreferenceForR()204 private Preference createAutoRevokeFooterPreferenceForR() { 205 Preference autoRevokePreference = new FixedSizeIconPreference( 206 getPreferenceManager().getContext(), true, true); 207 autoRevokePreference.setOrder(-1); 208 autoRevokePreference.setSingleLineTitle(false); 209 autoRevokePreference.setIcon(R.drawable.ic_info_outline_accent); 210 autoRevokePreference.setTitle(R.string.auto_revoke_permission_notification_title); 211 autoRevokePreference.setSummary(R.string.auto_revoke_setting_subtitle); 212 autoRevokePreference.setOnPreferenceClickListener(preference -> { 213 mViewModel.showAutoRevoke(this, UnusedAppsFragment.createArgs( 214 getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID))); 215 return true; 216 }); 217 return autoRevokePreference; 218 } 219 220 @Override showPermissionApps(String permissionGroupName)221 public void showPermissionApps(String permissionGroupName) { 222 // If we return to this page within a reasonable time, prioritize loading data from the 223 // permission group whose page we are going to, as that is group most likely to have changed 224 getPermGroupsLiveData().setFirstLoadGroup(permissionGroupName); 225 mViewModel.showPermissionApps(this, PermissionAppsFragment.createArgs( 226 permissionGroupName, getArguments().getLong(EXTRA_SESSION_ID))); 227 } 228 } 229