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 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; 20 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; 21 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW; 22 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS; 23 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND; 24 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY; 25 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED; 26 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT; 27 28 import android.Manifest; 29 import android.app.Activity; 30 import android.app.AlertDialog; 31 import android.app.Dialog; 32 import android.content.Context; 33 import android.content.DialogInterface; 34 import android.content.Intent; 35 import android.content.pm.PackageManager; 36 import android.graphics.drawable.Drawable; 37 import android.hardware.SensorPrivacyManager; 38 import android.os.Build; 39 import android.os.Bundle; 40 import android.os.UserHandle; 41 import android.text.BidiFormatter; 42 import android.util.Log; 43 import android.view.View; 44 import android.widget.RadioButton; 45 46 import androidx.annotation.NonNull; 47 import androidx.annotation.Nullable; 48 import androidx.annotation.RequiresApi; 49 import androidx.core.content.res.TypedArrayUtils; 50 import androidx.fragment.app.DialogFragment; 51 import androidx.fragment.app.Fragment; 52 import androidx.lifecycle.ViewModelProvider; 53 import androidx.preference.PreferenceCategory; 54 import androidx.preference.PreferenceGroup; 55 import androidx.preference.PreferenceScreen; 56 import androidx.preference.PreferenceViewHolder; 57 import androidx.preference.TwoStatePreference; 58 59 import com.android.car.ui.AlertDialogBuilder; 60 import com.android.modules.utils.build.SdkLevel; 61 import com.android.permission.flags.Flags; 62 import com.android.permissioncontroller.R; 63 import com.android.permissioncontroller.auto.AutoSettingsFrameFragment; 64 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler; 65 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel; 66 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ChangeRequest; 67 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModelFactory; 68 import com.android.permissioncontroller.permission.ui.v33.AdvancedConfirmDialogArgs; 69 import com.android.permissioncontroller.permission.utils.KotlinUtils; 70 import com.android.permissioncontroller.permission.utils.LocationUtils; 71 import com.android.permissioncontroller.permission.utils.PackageRemovalMonitor; 72 import com.android.permissioncontroller.permission.utils.Utils; 73 import com.android.settingslib.RestrictedLockUtils; 74 75 import kotlin.Pair; 76 77 import java.util.Collection; 78 import java.util.List; 79 import java.util.Map; 80 81 /** Settings related to a particular permission for the given app. */ 82 public class AutoAppPermissionFragment extends AutoSettingsFrameFragment 83 implements AppPermissionViewModel.ConfirmDialogShowingFragment { 84 85 private static final String LOG_TAG = "AppPermissionFragment"; 86 private static final long POST_DELAY_MS = 20; 87 private static final String BLOCKED_APP_PREF_KEY = "blocked_app"; 88 private static final String REQUIRED_APP_PREF_KEY = "required_app"; 89 90 @NonNull 91 private TwoStatePreference mAllowPermissionPreference; 92 @NonNull 93 private TwoStatePreference mAlwaysPermissionPreference; 94 @NonNull 95 private TwoStatePreference mForegroundOnlyPermissionPreference; 96 @NonNull 97 private TwoStatePreference mDenyPermissionPreference; 98 @NonNull 99 private AutoTwoTargetPreference mDetailsPreference; 100 101 @NonNull 102 private AppPermissionViewModel mViewModel; 103 @NonNull 104 private String mPackageName; 105 @NonNull 106 private String mPermGroupName; 107 @NonNull 108 private UserHandle mUser; 109 @NonNull 110 private String mPackageLabel; 111 @NonNull 112 private String mPermGroupLabel; 113 private Drawable mPackageIcon; 114 115 private SensorPrivacyManager mSensorPrivacyManager; 116 private Collection<String> mAutomotiveLocationBypassAllowlist; 117 private List<String> mCameraPrivacyAllowlist; 118 119 /** 120 * Listens for changes to the app the permission is currently getting granted to. {@code null} 121 * when unregistered. 122 */ 123 @Nullable 124 private PackageRemovalMonitor mPackageRemovalMonitor; 125 126 /** 127 * Returns a new {@link AutoAppPermissionFragment}. 128 * 129 * @param packageName the package name for which the permission is being changed 130 * @param permName the name of the permission being changed 131 * @param groupName the name of the permission group being changed 132 * @param userHandle the user for which the permission is being changed 133 */ 134 @NonNull newInstance(@onNull String packageName, @NonNull String permName, @Nullable String groupName, @NonNull UserHandle userHandle, @NonNull long sessionId)135 public static AutoAppPermissionFragment newInstance(@NonNull String packageName, 136 @NonNull String permName, @Nullable String groupName, @NonNull UserHandle userHandle, 137 @NonNull long sessionId) { 138 AutoAppPermissionFragment fragment = new AutoAppPermissionFragment(); 139 Bundle arguments = new Bundle(); 140 arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName); 141 if (groupName == null) { 142 arguments.putString(Intent.EXTRA_PERMISSION_NAME, permName); 143 } else { 144 arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName); 145 } 146 arguments.putParcelable(Intent.EXTRA_USER, userHandle); 147 arguments.putLong(EXTRA_SESSION_ID, sessionId); 148 fragment.setArguments(arguments); 149 return fragment; 150 } 151 152 @Override onCreatePreferences(Bundle bundle, String s)153 public void onCreatePreferences(Bundle bundle, String s) { 154 setPreferenceScreen(getPreferenceManager().createPreferenceScreen(requireContext())); 155 } 156 157 @Override onCreate(@ullable Bundle savedInstanceState)158 public void onCreate(@Nullable Bundle savedInstanceState) { 159 super.onCreate(savedInstanceState); 160 mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME); 161 mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME); 162 if (mPermGroupName == null) { 163 mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME); 164 } 165 mUser = getArguments().getParcelable(Intent.EXTRA_USER); 166 mPackageLabel = BidiFormatter.getInstance().unicodeWrap( 167 KotlinUtils.INSTANCE.getPackageLabel(getActivity().getApplication(), mPackageName, 168 mUser)); 169 mPermGroupLabel = KotlinUtils.INSTANCE.getPermGroupLabel(getContext(), 170 mPermGroupName).toString(); 171 mPackageIcon = KotlinUtils.INSTANCE.getBadgedPackageIcon(getActivity().getApplication(), 172 mPackageName, mUser); 173 setHeaderLabel( 174 requireContext().getString(R.string.app_permission_title, mPermGroupLabel)); 175 if (SdkLevel.isAtLeastV()) { 176 mSensorPrivacyManager = requireContext().getSystemService(SensorPrivacyManager.class); 177 mCameraPrivacyAllowlist = mSensorPrivacyManager.getCameraPrivacyAllowlist(); 178 if (Flags.addBannersToPrivacySensitiveAppsForAaos()) { 179 mAutomotiveLocationBypassAllowlist = 180 LocationUtils.getAutomotiveLocationBypassAllowlist(requireContext()); 181 } 182 } 183 } 184 185 @Override onViewCreated(@onNull View view, @Nullable Bundle savedInstanceState)186 public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 187 super.onViewCreated(view, savedInstanceState); 188 189 PreferenceScreen screen = getPreferenceScreen(); 190 screen.addPreference( 191 AutoPermissionsUtils.createHeaderPreference(requireContext(), 192 mPackageIcon, mPackageName, mPackageLabel)); 193 194 // Add permissions selector preferences. 195 PreferenceGroup permissionSelector = new PreferenceCategory(requireContext()); 196 permissionSelector.setTitle( 197 getString(R.string.app_permission_header, mPermGroupLabel)); 198 screen.addPreference(permissionSelector); 199 200 mAllowPermissionPreference = new SelectedPermissionPreference(requireContext()); 201 mAllowPermissionPreference.setTitle(R.string.app_permission_button_allow); 202 permissionSelector.addPreference(mAllowPermissionPreference); 203 204 mAlwaysPermissionPreference = new SelectedPermissionPreference(requireContext()); 205 mAlwaysPermissionPreference.setTitle(R.string.app_permission_button_allow_always); 206 permissionSelector.addPreference(mAlwaysPermissionPreference); 207 208 mForegroundOnlyPermissionPreference = new SelectedPermissionPreference(requireContext()); 209 mForegroundOnlyPermissionPreference.setTitle( 210 R.string.app_permission_button_allow_foreground); 211 permissionSelector.addPreference(mForegroundOnlyPermissionPreference); 212 213 mDenyPermissionPreference = new SelectedPermissionPreference(requireContext()); 214 mDenyPermissionPreference.setTitle(R.string.app_permission_button_deny); 215 permissionSelector.addPreference(mDenyPermissionPreference); 216 217 mAllowPermissionPreference.setOnPreferenceClickListener(v -> { 218 checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.ALLOW); 219 setResult(GrantPermissionsViewHandler.GRANTED_ALWAYS); 220 requestChange(ChangeRequest.GRANT_FOREGROUND, 221 APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW); 222 return true; 223 }); 224 mAlwaysPermissionPreference.setOnPreferenceClickListener(v -> { 225 checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.ALLOW_ALWAYS); 226 setResult(GrantPermissionsViewHandler.GRANTED_ALWAYS); 227 requestChange(ChangeRequest.GRANT_BOTH, 228 APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS); 229 return true; 230 }); 231 mForegroundOnlyPermissionPreference.setOnPreferenceClickListener(v -> { 232 checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND); 233 setResult(GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY); 234 requestChange(ChangeRequest.GRANT_FOREGROUND_ONLY, 235 APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND); 236 return true; 237 }); 238 mDenyPermissionPreference.setOnPreferenceClickListener(v -> { 239 checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType.DENY); 240 setResult(GrantPermissionsViewHandler.DENIED); 241 requestChange(ChangeRequest.REVOKE_BOTH, 242 APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY); 243 return true; 244 }); 245 246 mDetailsPreference = new AutoTwoTargetPreference(requireContext()); 247 screen.addPreference(mDetailsPreference); 248 } 249 250 @Override onStart()251 public void onStart() { 252 super.onStart(); 253 Activity activity = requireActivity(); 254 255 // Get notified when the package is removed. 256 mPackageRemovalMonitor = new PackageRemovalMonitor(requireContext(), mPackageName) { 257 @Override 258 public void onPackageRemoved() { 259 Log.w(LOG_TAG, mPackageName + " was uninstalled"); 260 activity.setResult(Activity.RESULT_CANCELED); 261 activity.finish(); 262 } 263 }; 264 mPackageRemovalMonitor.register(); 265 266 // Check if the package was removed while this activity was not started. 267 try { 268 activity.createPackageContextAsUser(mPackageName, /* flags= */ 0, 269 mUser).getPackageManager().getPackageInfo(mPackageName, 270 /* flags= */ 0); 271 } catch (PackageManager.NameNotFoundException e) { 272 Log.w(LOG_TAG, mPackageName + " was uninstalled while this activity was stopped", e); 273 activity.setResult(Activity.RESULT_CANCELED); 274 activity.finish(); 275 } 276 277 long sessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID); 278 AppPermissionViewModelFactory factory = new AppPermissionViewModelFactory( 279 getActivity().getApplication(), mPackageName, mPermGroupName, mUser, sessionId); 280 mViewModel = new ViewModelProvider(this, factory).get(AppPermissionViewModel.class); 281 mViewModel.getButtonStateLiveData().observe(this, this::setRadioButtonsState); 282 mViewModel.getDetailResIdLiveData().observe(this, this::setDetail); 283 mViewModel.getShowAdminSupportLiveData().observe(this, this::setAdminSupportDetail); 284 if (SdkLevel.isAtLeastV()) { 285 if (Manifest.permission_group.CAMERA.equals(mPermGroupName)) { 286 mViewModel.getSensorStatusLiveData().observe(this, this::setSensorStatus); 287 } 288 if (Flags.addBannersToPrivacySensitiveAppsForAaos()) { 289 if (Manifest.permission_group.LOCATION.equals(mPermGroupName)) { 290 mViewModel.getSensorStatusLiveData().observe(this, this::setSensorStatus); 291 } 292 if (Manifest.permission_group.MICROPHONE.equals(mPermGroupName)) { 293 mViewModel.getSensorStatusLiveData().observe(this, this::setSensorStatus); 294 } 295 } 296 } 297 } 298 299 @Override onStop()300 public void onStop() { 301 super.onStop(); 302 303 if (mPackageRemovalMonitor != null) { 304 mPackageRemovalMonitor.unregister(); 305 mPackageRemovalMonitor = null; 306 } 307 } 308 309 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) setSensorStatus(Boolean sensorStatus)310 private void setSensorStatus(Boolean sensorStatus) { 311 Boolean isRequiredApp = null; 312 Boolean isRequiredAppCard = null; 313 if (Manifest.permission_group.CAMERA.equals(mPermGroupName)) { 314 int state = mSensorPrivacyManager.getSensorPrivacyState( 315 SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE, 316 SensorPrivacyManager.Sensors.CAMERA); 317 isRequiredApp = mCameraPrivacyAllowlist.contains(mPackageName); 318 isRequiredAppCard = 319 state == SensorPrivacyManager.StateTypes.ENABLED_EXCEPT_ALLOWLISTED_APPS 320 && isRequiredApp; 321 } else if (Manifest.permission_group.LOCATION.equals(mPermGroupName)) { 322 isRequiredApp = mAutomotiveLocationBypassAllowlist.contains(mPackageName); 323 isRequiredAppCard = 324 isRequiredApp && LocationUtils.isAutomotiveLocationBypassEnabled( 325 getPreferenceManager().getContext()); 326 } else if (Manifest.permission_group.MICROPHONE.equals(mPermGroupName)) { 327 isRequiredApp = false; 328 isRequiredAppCard = false; 329 } 330 331 if (isRequiredApp != null && isRequiredAppCard != null) { 332 if (sensorStatus) { 333 setSensorCard(isRequiredAppCard, isRequiredApp); 334 } else { 335 removeSensorCard(isRequiredAppCard); 336 } 337 } 338 } 339 340 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) setSensorCard(boolean isRequiredAppCard, boolean isRequiredApp)341 private void setSensorCard(boolean isRequiredAppCard, boolean isRequiredApp) { 342 if (isRequiredAppCard) { 343 setRequiredAppCard(); 344 } else { 345 setBlockedAppCard(isRequiredApp); 346 } 347 } 348 setRequiredAppCard()349 private void setRequiredAppCard() { 350 AutoCardViewPreference sensorCard = findPreference(REQUIRED_APP_PREF_KEY); 351 if (sensorCard == null) { 352 sensorCard = createRequiredAppCard(); 353 if (getPreferenceScreen() != null) { 354 getPreferenceScreen().addPreference(sensorCard); 355 } 356 } 357 sensorCard.setVisible(true); 358 } 359 setBlockedAppCard(boolean isRequiredApp)360 private void setBlockedAppCard(boolean isRequiredApp) { 361 AutoCardViewPreference sensorCard = findPreference(BLOCKED_APP_PREF_KEY); 362 if (sensorCard == null) { 363 sensorCard = createBlockedAppCard(isRequiredApp); 364 if (getPreferenceScreen() != null) { 365 getPreferenceScreen().addPreference(sensorCard); 366 } 367 } 368 sensorCard.setVisible(true); 369 } 370 createRequiredAppCard()371 private AutoCardViewPreference createRequiredAppCard() { 372 Context context = getPreferenceManager().getContext(); 373 AutoCardViewPreference sensorCard = new AutoCardViewPreference(context); 374 sensorCard.setKey(REQUIRED_APP_PREF_KEY); 375 sensorCard.setIcon(KotlinUtils.INSTANCE.getPermGroupIcon(context, mPermGroupName)); 376 sensorCard.setTitle(context.getString(R.string.automotive_required_app_title)); 377 sensorCard.setSummary(context.getString(R.string.automotive_required_app_summary)); 378 sensorCard.setVisible(true); 379 sensorCard.setOrder(-1); 380 return sensorCard; 381 } 382 createBlockedAppCard(boolean isRequiredApp)383 private AutoCardViewPreference createBlockedAppCard(boolean isRequiredApp) { 384 Context context = getPreferenceManager().getContext(); 385 386 AutoCardViewPreference sensorCard = new AutoCardViewPreference(context); 387 sensorCard.setKey(BLOCKED_APP_PREF_KEY); 388 sensorCard.setIcon(Utils.getBlockedIcon(mPermGroupName)); 389 sensorCard.setTitle(context.getString(Utils.getBlockedTitleAutomotive(mPermGroupName))); 390 if (isRequiredApp) { 391 sensorCard.setSummary(context.getString( 392 R.string.automotive_blocked_required_app_summary)); 393 } else { 394 sensorCard.setSummary(context.getString( 395 R.string.automotive_blocked_infotainment_app_summary)); 396 } 397 sensorCard.setVisible(true); 398 sensorCard.setOrder(-1); 399 return sensorCard; 400 } 401 402 @RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM) removeSensorCard(boolean isRequiredAppCard)403 private void removeSensorCard(boolean isRequiredAppCard) { 404 if (isRequiredAppCard) { 405 removeRequiredAppCard(); 406 } else { 407 removeBlockedAppCard(); 408 } 409 } 410 removeRequiredAppCard()411 private void removeRequiredAppCard() { 412 AutoCardViewPreference sensorCard = findPreference(REQUIRED_APP_PREF_KEY); 413 if (sensorCard != null) { 414 sensorCard.setVisible(false); 415 } 416 } 417 removeBlockedAppCard()418 private void removeBlockedAppCard() { 419 AutoCardViewPreference sensorCard = findPreference(BLOCKED_APP_PREF_KEY); 420 if (sensorCard != null) { 421 sensorCard.setVisible(false); 422 } 423 } 424 425 @Override showConfirmDialog(ChangeRequest changeRequest, int messageId, int buttonPressed, boolean oneTime)426 public void showConfirmDialog(ChangeRequest changeRequest, int messageId, 427 int buttonPressed, boolean oneTime) { 428 Bundle args = new Bundle(); 429 430 args.putInt(ConfirmDialog.MSG, messageId); 431 args.putSerializable(ConfirmDialog.CHANGE_REQUEST, changeRequest); 432 args.putSerializable(ConfirmDialog.BUTTON, buttonPressed); 433 434 ConfirmDialog confirmDialog = new ConfirmDialog(); 435 confirmDialog.setArguments(args); 436 confirmDialog.setTargetFragment(this, 0); 437 confirmDialog.show(requireFragmentManager().beginTransaction(), 438 ConfirmDialog.class.getName()); 439 } 440 setResult(@rantPermissionsViewHandler.Result int result)441 private void setResult(@GrantPermissionsViewHandler.Result int result) { 442 Intent intent = new Intent() 443 .putExtra(EXTRA_RESULT_PERMISSION_INTERACTED, 444 requireArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME)) 445 .putExtra(EXTRA_RESULT_PERMISSION_RESULT, result); 446 requireActivity().setResult(Activity.RESULT_OK, intent); 447 } 448 setRadioButtonsState( Map<AppPermissionViewModel.ButtonType, AppPermissionViewModel.ButtonState> states)449 private void setRadioButtonsState( 450 Map<AppPermissionViewModel.ButtonType, AppPermissionViewModel.ButtonState> states) { 451 setButtonState(mAllowPermissionPreference, 452 states.get(AppPermissionViewModel.ButtonType.ALLOW)); 453 setButtonState(mAlwaysPermissionPreference, 454 states.get(AppPermissionViewModel.ButtonType.ALLOW_ALWAYS)); 455 setButtonState(mForegroundOnlyPermissionPreference, 456 states.get(AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND)); 457 setButtonState(mDenyPermissionPreference, 458 states.get(AppPermissionViewModel.ButtonType.DENY)); 459 } 460 setButtonState(TwoStatePreference button, AppPermissionViewModel.ButtonState state)461 private void setButtonState(TwoStatePreference button, 462 AppPermissionViewModel.ButtonState state) { 463 button.setVisible(state.isShown()); 464 if (state.isShown()) { 465 button.setChecked(state.isChecked()); 466 button.setEnabled(state.isEnabled()); 467 } 468 } 469 470 /** 471 * Helper method to handle the UX edge case where the confirmation dialog is shown and two 472 * buttons are selected at once. This happens since the Auto UI doesn't use a proper radio 473 * group, so there is nothing that enforces that tapping on a button unchecks a previously 474 * checked button. Apart from this case, this UI is not necessary since the UI is entirely 475 * driven by the ViewModel. 476 */ checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType buttonType)477 private void checkOnlyOneButtonOverride(AppPermissionViewModel.ButtonType buttonType) { 478 mAllowPermissionPreference.setChecked( 479 buttonType == AppPermissionViewModel.ButtonType.ALLOW); 480 mAlwaysPermissionPreference.setChecked( 481 buttonType == AppPermissionViewModel.ButtonType.ALLOW_ALWAYS); 482 mForegroundOnlyPermissionPreference.setChecked( 483 buttonType == AppPermissionViewModel.ButtonType.ALLOW_FOREGROUND); 484 mDenyPermissionPreference.setChecked(buttonType == AppPermissionViewModel.ButtonType.DENY); 485 } 486 setDetail(Pair<Integer, Integer> detailResIds)487 private void setDetail(Pair<Integer, Integer> detailResIds) { 488 if (detailResIds == null) { 489 mDetailsPreference.setVisible(false); 490 return; 491 } 492 if (detailResIds.getSecond() != null) { 493 mDetailsPreference.setWidgetLayoutResource(R.layout.settings_preference_widget); 494 mDetailsPreference.setOnSecondTargetClickListener( 495 v -> showAllPermissions(mPermGroupName)); 496 mDetailsPreference.setSummary( 497 getString(detailResIds.getFirst(), detailResIds.getSecond())); 498 } else { 499 mDetailsPreference.setSummary(detailResIds.getFirst()); 500 } 501 } 502 503 /** 504 * Show all individual permissions in this group in a new fragment. 505 */ showAllPermissions(@onNull String filterGroup)506 private void showAllPermissions(@NonNull String filterGroup) { 507 Fragment frag = AutoAllAppPermissionsFragment.newInstance(mPackageName, 508 filterGroup, mUser, 509 getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID)); 510 requireFragmentManager().beginTransaction() 511 .replace(android.R.id.content, frag) 512 .addToBackStack("AllPerms") 513 .commit(); 514 } 515 setAdminSupportDetail(RestrictedLockUtils.EnforcedAdmin admin)516 private void setAdminSupportDetail(RestrictedLockUtils.EnforcedAdmin admin) { 517 if (admin != null) { 518 mDetailsPreference.setWidgetLayoutResource(R.layout.info_preference_widget); 519 mDetailsPreference.setOnSecondTargetClickListener(v -> 520 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), admin) 521 ); 522 } 523 } 524 525 /** 526 * Request to grant/revoke permissions group. 527 */ requestChange(ChangeRequest changeRequest, int buttonClicked)528 private void requestChange(ChangeRequest changeRequest, 529 int buttonClicked) { 530 mViewModel.requestChange(/* setOneTime= */false, /* fragment= */ this, 531 /* defaultDeny= */this, changeRequest, buttonClicked); 532 } 533 534 /** Preference used to represent apps that can be picked as a default app. */ 535 private static class SelectedPermissionPreference extends TwoStatePreference { 536 SelectedPermissionPreference(Context context)537 SelectedPermissionPreference(Context context) { 538 super(context, null, 539 TypedArrayUtils.getAttr(context, androidx.preference.R.attr.preferenceStyle, 540 android.R.attr.preferenceStyle)); 541 setPersistent(false); 542 setLayoutResource(R.layout.car_radio_button_preference); 543 setWidgetLayoutResource(R.layout.radio_button_preference_widget); 544 } 545 546 @Override onBindViewHolder(PreferenceViewHolder holder)547 public void onBindViewHolder(PreferenceViewHolder holder) { 548 super.onBindViewHolder(holder); 549 550 RadioButton radioButton = (RadioButton) holder.findViewById(R.id.radio_button); 551 radioButton.setChecked(isChecked()); 552 } 553 } 554 555 /** 556 * A dialog warning the user that they are about to deny a permission that was granted by 557 * default. 558 * 559 * @see #showConfirmDialog(ChangeRequest, int, int, boolean) 560 */ 561 public static class ConfirmDialog extends DialogFragment { 562 private static final String MSG = ConfirmDialog.class.getName() + ".arg.msg"; 563 private static final String CHANGE_REQUEST = ConfirmDialog.class.getName() 564 + ".arg.changeRequest"; 565 private static final String BUTTON = ConfirmDialog.class.getName() 566 + ".arg.button"; 567 private static int sCode = APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW; 568 569 @NonNull 570 @Override onCreateDialog(Bundle savedInstanceState)571 public Dialog onCreateDialog(Bundle savedInstanceState) { 572 // TODO(b/229024576): This code is duplicated, refactor ConfirmDialog for easier 573 // NFF sharing 574 boolean isGrantFileAccess = getArguments().getSerializable(CHANGE_REQUEST) 575 == ChangeRequest.GRANT_ALL_FILE_ACCESS; 576 boolean isGrantStorageSupergroup = getArguments().getSerializable(CHANGE_REQUEST) 577 == ChangeRequest.GRANT_STORAGE_SUPERGROUP; 578 int positiveButtonStringResId = R.string.grant_dialog_button_deny_anyway; 579 if (isGrantFileAccess || isGrantStorageSupergroup) { 580 positiveButtonStringResId = R.string.grant_dialog_button_allow; 581 } 582 AutoAppPermissionFragment fragment = (AutoAppPermissionFragment) getTargetFragment(); 583 return new AlertDialogBuilder(getContext()) 584 .setMessage(requireArguments().getInt(MSG)) 585 .setNegativeButton(R.string.cancel, 586 (dialog, which) -> dialog.cancel()) 587 .setPositiveButton(positiveButtonStringResId, 588 (dialog, which) -> { 589 if (isGrantFileAccess) { 590 fragment.mViewModel.setAllFilesAccess(true); 591 fragment.mViewModel.requestChange(false, fragment, 592 fragment, ChangeRequest.GRANT_BOTH, sCode); 593 } else if (isGrantStorageSupergroup) { 594 fragment.mViewModel.requestChange(false, fragment, 595 fragment, ChangeRequest.GRANT_BOTH, sCode); 596 } else { 597 fragment.mViewModel.onDenyAnyWay((ChangeRequest) 598 getArguments().getSerializable(CHANGE_REQUEST), 599 getArguments().getInt(BUTTON), 600 /* oneTime= */ false); 601 } 602 }) 603 .create(); 604 } 605 606 @Override onCancel(DialogInterface dialog)607 public void onCancel(DialogInterface dialog) { 608 AutoAppPermissionFragment fragment = (AutoAppPermissionFragment) getTargetFragment(); 609 fragment.setRadioButtonsState(fragment.mViewModel.getButtonStateLiveData().getValue()); 610 } 611 } 612 613 @Override 614 public void showAdvancedConfirmDialog(AdvancedConfirmDialogArgs args) { 615 AlertDialog.Builder b = new AlertDialog.Builder(getContext()) 616 .setIcon(args.getIconId()) 617 .setMessage(args.getMessageId()) 618 .setOnCancelListener((DialogInterface dialog) -> { 619 setRadioButtonsState(mViewModel.getButtonStateLiveData().getValue()); 620 }) 621 .setNegativeButton(args.getNegativeButtonTextId(), 622 (DialogInterface dialog, int which) -> { 623 setRadioButtonsState(mViewModel.getButtonStateLiveData().getValue()); 624 }) 625 .setPositiveButton(args.getPositiveButtonTextId(), 626 (DialogInterface dialog, int which) -> { 627 mViewModel.requestChange(args.getSetOneTime(), 628 AutoAppPermissionFragment.this, AutoAppPermissionFragment.this, 629 args.getChangeRequest(), args.getButtonClicked()); 630 }); 631 if (args.getTitleId() != 0) { 632 b.setTitle(args.getTitleId()); 633 } 634 b.show(); 635 } 636 } 637