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.permissioncontroller.permission.ui.handheld; 18 19 import static android.Manifest.permission_group.STORAGE; 20 21 import static com.android.permissioncontroller.Constants.EXTRA_SESSION_ID; 22 import static com.android.permissioncontroller.Constants.INVALID_SESSION_ID; 23 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW; 24 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS; 25 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND; 26 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ASK_EVERY_TIME; 27 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY; 28 import static com.android.permissioncontroller.PermissionControllerStatsLog.APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY_FOREGROUND; 29 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED; 30 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN; 31 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS; 32 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY; 33 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_CALLER_NAME; 34 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_INTERACTED; 35 import static com.android.permissioncontroller.permission.ui.ManagePermissionsActivity.EXTRA_RESULT_PERMISSION_RESULT; 36 import static com.android.permissioncontroller.permission.ui.handheld.UtilsKt.pressBack; 37 38 import android.app.ActionBar; 39 import android.app.Activity; 40 import android.app.AlertDialog; 41 import android.app.Dialog; 42 import android.content.Context; 43 import android.content.DialogInterface; 44 import android.content.Intent; 45 import android.content.pm.PackageManager; 46 import android.graphics.drawable.Drawable; 47 import android.os.Bundle; 48 import android.os.Handler; 49 import android.os.Looper; 50 import android.os.UserHandle; 51 import android.text.BidiFormatter; 52 import android.util.Log; 53 import android.view.LayoutInflater; 54 import android.view.MenuItem; 55 import android.view.View; 56 import android.view.ViewGroup; 57 import android.widget.ImageView; 58 import android.widget.RadioButton; 59 import android.widget.TextView; 60 import android.widget.Toast; 61 62 import androidx.annotation.NonNull; 63 import androidx.annotation.Nullable; 64 import androidx.annotation.StringRes; 65 import androidx.core.widget.NestedScrollView; 66 import androidx.fragment.app.DialogFragment; 67 import androidx.lifecycle.ViewModelProvider; 68 69 import com.android.permissioncontroller.R; 70 import com.android.permissioncontroller.permission.data.FullStoragePermissionAppsLiveData.FullStoragePackageState; 71 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler; 72 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel; 73 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonState; 74 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ButtonType; 75 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModel.ChangeRequest; 76 import com.android.permissioncontroller.permission.ui.model.AppPermissionViewModelFactory; 77 import com.android.permissioncontroller.permission.utils.KotlinUtils; 78 import com.android.permissioncontroller.permission.utils.Utils; 79 import com.android.settingslib.RestrictedLockUtils; 80 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 81 import com.android.settingslib.widget.ActionBarShadowController; 82 83 import java.util.Map; 84 import java.util.Objects; 85 86 import kotlin.Pair; 87 88 /** 89 * Show and manage a single permission group for an app. 90 * 91 * <p>Allows the user to control whether the app is granted the permission. 92 */ 93 public class AppPermissionFragment extends SettingsWithLargeHeader 94 implements AppPermissionViewModel.ConfirmDialogShowingFragment { 95 private static final String LOG_TAG = "AppPermissionFragment"; 96 private static final long POST_DELAY_MS = 20; 97 98 static final String GRANT_CATEGORY = "grant_category"; 99 100 private @NonNull AppPermissionViewModel mViewModel; 101 private @NonNull RadioButton mAllowButton; 102 private @NonNull RadioButton mAllowAlwaysButton; 103 private @NonNull RadioButton mAllowForegroundButton; 104 private @NonNull RadioButton mAskOneTimeButton; 105 private @NonNull RadioButton mAskButton; 106 private @NonNull RadioButton mDenyButton; 107 private @NonNull RadioButton mDenyForegroundButton; 108 private @NonNull View mDivider; 109 private @NonNull ViewGroup mWidgetFrame; 110 private @NonNull TextView mPermissionDetails; 111 private @NonNull NestedScrollView mNestedScrollView; 112 private @NonNull String mPackageName; 113 private @NonNull String mPermGroupName; 114 private @NonNull UserHandle mUser; 115 private boolean mIsStorageGroup; 116 private boolean mIsInitialLoad; 117 private long mSessionId; 118 119 private @NonNull String mPackageLabel; 120 private @NonNull String mPermGroupLabel; 121 private Drawable mPackageIcon; 122 private Utils.ForegroundCapableType mForegroundCapableType; 123 124 /** 125 * Create a bundle with the arguments needed by this fragment 126 * 127 * @param packageName The name of the package 128 * @param permName The name of the permission whose group this fragment is for (optional) 129 * @param groupName The name of the permission group (required if permName not specified) 130 * @param userHandle The user of the app permission group 131 * @param caller The name of the fragment we called from 132 * @param sessionId The current session ID 133 * @param grantCategory The grant status of this app permission group. Used to initially set 134 * the button state 135 * @return A bundle with all of the args placed 136 */ createArgs(@onNull String packageName, @Nullable String permName, @Nullable String groupName, @NonNull UserHandle userHandle, @Nullable String caller, long sessionId, @Nullable String grantCategory)137 public static Bundle createArgs(@NonNull String packageName, 138 @Nullable String permName, @Nullable String groupName, 139 @NonNull UserHandle userHandle, @Nullable String caller, long sessionId, @Nullable 140 String grantCategory) { 141 Bundle arguments = new Bundle(); 142 arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName); 143 if (groupName == null) { 144 arguments.putString(Intent.EXTRA_PERMISSION_NAME, permName); 145 } else { 146 arguments.putString(Intent.EXTRA_PERMISSION_GROUP_NAME, groupName); 147 } 148 arguments.putParcelable(Intent.EXTRA_USER, userHandle); 149 arguments.putString(EXTRA_CALLER_NAME, caller); 150 arguments.putLong(EXTRA_SESSION_ID, sessionId); 151 arguments.putString(GRANT_CATEGORY, grantCategory); 152 return arguments; 153 } 154 155 @Override onCreate(Bundle savedInstanceState)156 public void onCreate(Bundle savedInstanceState) { 157 super.onCreate(savedInstanceState); 158 159 setHasOptionsMenu(true); 160 ActionBar ab = getActivity().getActionBar(); 161 if (ab != null) { 162 ab.setDisplayHomeAsUpEnabled(true); 163 } 164 165 mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME); 166 mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_GROUP_NAME); 167 if (mPermGroupName == null) { 168 mPermGroupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME); 169 } 170 mIsStorageGroup = Objects.equals(mPermGroupName, STORAGE); 171 mUser = getArguments().getParcelable(Intent.EXTRA_USER); 172 mPackageLabel = BidiFormatter.getInstance().unicodeWrap( 173 KotlinUtils.INSTANCE.getPackageLabel(getActivity().getApplication(), mPackageName, 174 mUser)); 175 mPermGroupLabel = KotlinUtils.INSTANCE.getPermGroupLabel(getContext(), 176 mPermGroupName).toString(); 177 mPackageIcon = KotlinUtils.INSTANCE.getBadgedPackageIcon(getActivity().getApplication(), 178 mPackageName, mUser); 179 try { 180 mForegroundCapableType = Utils.getForegroundCapableType(getContext(), mPackageName); 181 } catch (PackageManager.NameNotFoundException e) { 182 Log.e(LOG_TAG, "Package " + mPackageName + " not found", e); 183 } 184 185 mSessionId = getArguments().getLong(EXTRA_SESSION_ID, INVALID_SESSION_ID); 186 187 AppPermissionViewModelFactory factory = new AppPermissionViewModelFactory( 188 getActivity().getApplication(), mPackageName, mPermGroupName, mUser, mSessionId, 189 mForegroundCapableType); 190 mViewModel = new ViewModelProvider(this, factory).get(AppPermissionViewModel.class); 191 Handler delayHandler = new Handler(Looper.getMainLooper()); 192 mViewModel.getButtonStateLiveData().observe(this, buttonState -> { 193 if (mIsInitialLoad) { 194 setRadioButtonsState(buttonState); 195 } else { 196 delayHandler.removeCallbacksAndMessages(null); 197 delayHandler.postDelayed(() -> setRadioButtonsState(buttonState), POST_DELAY_MS); 198 } 199 }); 200 mViewModel.getDetailResIdLiveData().observe(this, this::setDetail); 201 mViewModel.getShowAdminSupportLiveData().observe(this, this::setAdminSupportDetail); 202 if (mIsStorageGroup) { 203 mViewModel.getFullStorageStateLiveData().observe(this, this::setSpecialStorageState); 204 } 205 } 206 207 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)208 public View onCreateView(LayoutInflater inflater, ViewGroup container, 209 Bundle savedInstanceState) { 210 Context context = getContext(); 211 ViewGroup root = (ViewGroup) inflater.inflate(R.layout.app_permission, container, false); 212 213 mIsInitialLoad = true; 214 215 setHeader(mPackageIcon, mPackageLabel, null, null, false); 216 updateHeader(root.requireViewById(R.id.large_header)); 217 218 ((TextView) root.requireViewById(R.id.permission_message)).setText( 219 context.getString(R.string.app_permission_header, mPermGroupLabel)); 220 221 String caller = getArguments().getString(EXTRA_CALLER_NAME); 222 223 TextView footer1Link = root.requireViewById(R.id.footer_link_1); 224 footer1Link.setText(context.getString(R.string.app_permission_footer_app_permissions_link, 225 mPackageLabel)); 226 setBottomLinkState(footer1Link, caller, Intent.ACTION_MANAGE_APP_PERMISSIONS); 227 228 TextView footer2Link = root.requireViewById(R.id.footer_link_2); 229 footer2Link.setText(context.getString(R.string.app_permission_footer_permission_apps_link)); 230 setBottomLinkState(footer2Link, caller, Intent.ACTION_MANAGE_PERMISSION_APPS); 231 232 mAllowButton = root.requireViewById(R.id.allow_radio_button); 233 mAllowAlwaysButton = root.requireViewById(R.id.allow_always_radio_button); 234 mAllowForegroundButton = root.requireViewById(R.id.allow_foreground_only_radio_button); 235 mAskOneTimeButton = root.requireViewById(R.id.ask_one_time_radio_button); 236 mAskButton = root.requireViewById(R.id.ask_radio_button); 237 mDenyButton = root.requireViewById(R.id.deny_radio_button); 238 mDenyForegroundButton = root.requireViewById(R.id.deny_foreground_radio_button); 239 mDivider = root.requireViewById(R.id.two_target_divider); 240 mWidgetFrame = root.requireViewById(R.id.widget_frame); 241 mPermissionDetails = root.requireViewById(R.id.permission_details); 242 243 mNestedScrollView = root.requireViewById(R.id.nested_scroll_view); 244 245 if (mViewModel.getButtonStateLiveData().getValue() != null) { 246 setRadioButtonsState(mViewModel.getButtonStateLiveData().getValue()); 247 } else { 248 mAllowButton.setVisibility(View.GONE); 249 mAllowAlwaysButton.setVisibility(View.GONE); 250 mAllowForegroundButton.setVisibility(View.GONE); 251 mAskOneTimeButton.setVisibility(View.GONE); 252 mAskButton.setVisibility(View.GONE); 253 mDenyButton.setVisibility(View.GONE); 254 mDenyForegroundButton.setVisibility(View.GONE); 255 } 256 257 if (mViewModel.getFullStorageStateLiveData().isInitialized() && mIsStorageGroup) { 258 setSpecialStorageState(mViewModel.getFullStorageStateLiveData().getValue(), root); 259 } else { 260 TextView storageFooter = root.requireViewById(R.id.footer_storage_special_app_access); 261 storageFooter.setVisibility(View.GONE); 262 } 263 264 getActivity().setTitle( 265 getPreferenceManager().getContext().getString(R.string.app_permission_title, 266 mPermGroupLabel)); 267 268 return root; 269 } 270 setBottomLinkState(TextView view, String caller, String action)271 private void setBottomLinkState(TextView view, String caller, String action) { 272 if ((Objects.equals(caller, AppPermissionGroupsFragment.class.getName()) 273 && action.equals(Intent.ACTION_MANAGE_APP_PERMISSIONS)) 274 || (Objects.equals(caller, PermissionAppsFragment.class.getName()) 275 && action.equals(Intent.ACTION_MANAGE_PERMISSION_APPS))) { 276 view.setVisibility(View.GONE); 277 } else { 278 view.setOnClickListener((v) -> { 279 Bundle args; 280 if (action.equals(Intent.ACTION_MANAGE_APP_PERMISSIONS)) { 281 args = AppPermissionGroupsFragment.createArgs(mPackageName, mUser, 282 mSessionId, true); 283 } else { 284 args = PermissionAppsFragment.createArgs(mPermGroupName, mSessionId); 285 } 286 mViewModel.showBottomLinkPage(this, action, args); 287 }); 288 } 289 } 290 setSpecialStorageState(FullStoragePackageState storageState)291 private void setSpecialStorageState(FullStoragePackageState storageState) { 292 setSpecialStorageState(storageState, getView()); 293 } 294 295 @Override onStart()296 public void onStart() { 297 super.onStart(); 298 299 ActionBar ab = getActivity().getActionBar(); 300 if (ab != null) { 301 ab.setElevation(0); 302 } 303 304 ActionBarShadowController.attachToView(getActivity(), getLifecycle(), mNestedScrollView); 305 } 306 307 @Override onOptionsItemSelected(MenuItem item)308 public boolean onOptionsItemSelected(MenuItem item) { 309 if (item.getItemId() == android.R.id.home) { 310 pressBack(this); 311 return true; 312 } 313 return super.onOptionsItemSelected(item); 314 } 315 setRadioButtonsState(Map<ButtonType, ButtonState> states)316 private void setRadioButtonsState(Map<ButtonType, ButtonState> states) { 317 if (states == null && mViewModel.getButtonStateLiveData().isInitialized()) { 318 pressBack(this); 319 Log.w(LOG_TAG, "invalid package " + mPackageName + " or perm group " 320 + mPermGroupName); 321 Toast.makeText( 322 getActivity(), R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show(); 323 return; 324 } else if (states == null) { 325 return; 326 } 327 328 mAllowButton.setOnClickListener((v) -> { 329 mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_FOREGROUND, 330 APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW); 331 setResult(GRANTED_ALWAYS); 332 }); 333 mAllowAlwaysButton.setOnClickListener((v) -> { 334 if (mIsStorageGroup) { 335 showConfirmDialog(ChangeRequest.GRANT_All_FILE_ACCESS, 336 R.string.special_file_access_dialog, -1, false); 337 } else { 338 mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_BOTH, 339 APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_ALWAYS); 340 } 341 setResult(GRANTED_ALWAYS); 342 }); 343 mAllowForegroundButton.setOnClickListener((v) -> { 344 if (mIsStorageGroup) { 345 mViewModel.setAllFilesAccess(false); 346 mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_BOTH, 347 APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW); 348 setResult(GRANTED_ALWAYS); 349 } else { 350 mViewModel.requestChange(false, this, this, ChangeRequest.GRANT_FOREGROUND_ONLY, 351 APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW_FOREGROUND); 352 setResult(GRANTED_FOREGROUND_ONLY); 353 } 354 }); 355 // mAskOneTimeButton only shows if checked hence should do nothing 356 mAskButton.setOnClickListener((v) -> { 357 mViewModel.requestChange(true, this, this, ChangeRequest.REVOKE_BOTH, 358 APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ASK_EVERY_TIME); 359 setResult(DENIED); 360 }); 361 mDenyButton.setOnClickListener((v) -> { 362 363 if (mViewModel.getFullStorageStateLiveData().getValue() != null 364 && !mViewModel.getFullStorageStateLiveData().getValue().isLegacy()) { 365 mViewModel.setAllFilesAccess(false); 366 } 367 mViewModel.requestChange(false, this, this, ChangeRequest.REVOKE_BOTH, 368 APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY); 369 setResult(DENIED_DO_NOT_ASK_AGAIN); 370 }); 371 mDenyForegroundButton.setOnClickListener((v) -> { 372 mViewModel.requestChange(false, this, this, ChangeRequest.REVOKE_FOREGROUND, 373 APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__DENY_FOREGROUND); 374 setResult(DENIED_DO_NOT_ASK_AGAIN); 375 }); 376 377 setButtonState(mAllowButton, states.get(ButtonType.ALLOW)); 378 setButtonState(mAllowAlwaysButton, states.get(ButtonType.ALLOW_ALWAYS)); 379 setButtonState(mAllowForegroundButton, states.get(ButtonType.ALLOW_FOREGROUND)); 380 setButtonState(mAskOneTimeButton, states.get(ButtonType.ASK_ONCE)); 381 setButtonState(mAskButton, states.get(ButtonType.ASK)); 382 setButtonState(mDenyButton, states.get(ButtonType.DENY)); 383 setButtonState(mDenyForegroundButton, states.get(ButtonType.DENY_FOREGROUND)); 384 385 mIsInitialLoad = false; 386 387 if (mViewModel.getFullStorageStateLiveData().isInitialized()) { 388 setSpecialStorageState(mViewModel.getFullStorageStateLiveData().getValue()); 389 } 390 } 391 setButtonState(RadioButton button, AppPermissionViewModel.ButtonState state)392 private void setButtonState(RadioButton button, AppPermissionViewModel.ButtonState state) { 393 int visible = state.isShown() ? View.VISIBLE : View.GONE; 394 button.setVisibility(visible); 395 if (state.isShown()) { 396 button.setChecked(state.isChecked()); 397 button.setEnabled(state.isEnabled()); 398 } 399 if (mIsInitialLoad) { 400 button.jumpDrawablesToCurrentState(); 401 } 402 } 403 setSpecialStorageState(FullStoragePackageState storageState, View v)404 private void setSpecialStorageState(FullStoragePackageState storageState, View v) { 405 if (v == null) { 406 return; 407 } 408 409 TextView textView = v.requireViewById(R.id.footer_storage_special_app_access); 410 if (mAllowButton == null || !mIsStorageGroup) { 411 textView.setVisibility(View.GONE); 412 return; 413 } 414 415 mAllowAlwaysButton.setText(R.string.app_permission_button_allow_all_files); 416 mAllowForegroundButton.setText(R.string.app_permission_button_allow_media_only); 417 418 if (storageState == null) { 419 textView.setVisibility(View.GONE); 420 return; 421 } 422 423 if (storageState.isLegacy()) { 424 mAllowButton.setText(R.string.app_permission_button_allow_all_files); 425 textView.setVisibility(View.GONE); 426 return; 427 } 428 429 textView.setText(R.string.app_permission_footer_special_file_access); 430 textView.setVisibility(View.VISIBLE); 431 } 432 setResult(@rantPermissionsViewHandler.Result int result)433 private void setResult(@GrantPermissionsViewHandler.Result int result) { 434 Intent intent = new Intent() 435 .putExtra(EXTRA_RESULT_PERMISSION_INTERACTED, mPermGroupName) 436 .putExtra(EXTRA_RESULT_PERMISSION_RESULT, result); 437 getActivity().setResult(Activity.RESULT_OK, intent); 438 } 439 setDetail(Pair<Integer, Integer> detailResIds)440 private void setDetail(Pair<Integer, Integer> detailResIds) { 441 if (detailResIds == null) { 442 mWidgetFrame.setVisibility(View.GONE); 443 mDivider.setVisibility(View.GONE); 444 return; 445 } 446 mWidgetFrame.setVisibility(View.VISIBLE); 447 if (detailResIds.getSecond() != null) { 448 // If the permissions are individually controlled, also show a link to the page that 449 // lets you control them. 450 mDivider.setVisibility(View.VISIBLE); 451 showRightIcon(R.drawable.ic_settings); 452 Bundle args = AllAppPermissionsFragment.createArgs(mPackageName, mPermGroupName, mUser); 453 mWidgetFrame.setOnClickListener(v -> mViewModel.showAllPermissions(this, args)); 454 mPermissionDetails.setText(getPreferenceManager().getContext().getString( 455 detailResIds.getFirst(), detailResIds.getSecond())); 456 } else { 457 mPermissionDetails.setText(getPreferenceManager().getContext().getString( 458 detailResIds.getFirst())); 459 } 460 mPermissionDetails.setVisibility(View.VISIBLE); 461 462 } 463 setAdminSupportDetail(EnforcedAdmin admin)464 private void setAdminSupportDetail(EnforcedAdmin admin) { 465 if (admin != null) { 466 showRightIcon(R.drawable.ic_info); 467 mWidgetFrame.setOnClickListener(v -> 468 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(), admin) 469 ); 470 } else { 471 mWidgetFrame.removeAllViews(); 472 } 473 } 474 475 /** 476 * Show the given icon on the right of the first radio button. 477 * 478 * @param iconId the resourceId of the drawable to use. 479 */ showRightIcon(int iconId)480 private void showRightIcon(int iconId) { 481 mWidgetFrame.removeAllViews(); 482 ImageView imageView = new ImageView(getPreferenceManager().getContext()); 483 imageView.setImageResource(iconId); 484 mWidgetFrame.addView(imageView); 485 mWidgetFrame.setVisibility(View.VISIBLE); 486 } 487 488 /** 489 * Show a dialog that warns the user that she/he is about to revoke permissions that were 490 * granted by default, or that they are about to grant full file access to an app. 491 * 492 * 493 * The order of operation to revoke a permission granted by default is: 494 * 1. `showConfirmDialog` 495 * 1. [ConfirmDialog.onCreateDialog] 496 * 1. [AppPermissionViewModel.onDenyAnyWay] or [AppPermissionViewModel.onConfirmFileAccess] 497 * TODO: Remove once data can be passed between dialogs and fragments with nav component 498 * 499 * @param changeRequest Whether background or foreground should be changed 500 * @param messageId The Id of the string message to show 501 * @param buttonPressed Button which was pressed to initiate the dialog, one of 502 * AppPermissionFragmentActionReported.button_pressed constants 503 * @param oneTime Whether the one-time (ask) button was clicked rather than the deny 504 * button 505 */ 506 @Override showConfirmDialog(ChangeRequest changeRequest, @StringRes int messageId, int buttonPressed, boolean oneTime)507 public void showConfirmDialog(ChangeRequest changeRequest, @StringRes int messageId, 508 int buttonPressed, boolean oneTime) { 509 Bundle args = getArguments().deepCopy(); 510 args.putInt(ConfirmDialog.MSG, messageId); 511 args.putSerializable(ConfirmDialog.CHANGE_REQUEST, changeRequest); 512 args.putInt(ConfirmDialog.BUTTON, buttonPressed); 513 args.putBoolean(ConfirmDialog.ONE_TIME, oneTime); 514 ConfirmDialog defaultDenyDialog = new ConfirmDialog(); 515 defaultDenyDialog.setCancelable(true); 516 defaultDenyDialog.setArguments(args); 517 defaultDenyDialog.show(getChildFragmentManager().beginTransaction(), 518 ConfirmDialog.class.getName()); 519 } 520 521 /** 522 * A dialog warning the user that they are about to deny a permission that was granted by 523 * default, or that they are denying a permission on a Pre-M app 524 * 525 * @see AppPermissionViewModel.ConfirmDialogShowingFragment#showConfirmDialog(ChangeRequest, 526 * int, int, boolean) 527 * @see #showConfirmDialog(ChangeRequest, int, int) 528 */ 529 public static class ConfirmDialog extends DialogFragment { 530 static final String MSG = ConfirmDialog.class.getName() + ".arg.msg"; 531 static final String CHANGE_REQUEST = ConfirmDialog.class.getName() 532 + ".arg.changeRequest"; 533 private static final String KEY = ConfirmDialog.class.getName() + ".arg.key"; 534 private static final String BUTTON = ConfirmDialog.class.getName() + ".arg.button"; 535 private static final String ONE_TIME = ConfirmDialog.class.getName() + ".arg.onetime"; 536 private static int sCode = APP_PERMISSION_FRAGMENT_ACTION_REPORTED__BUTTON_PRESSED__ALLOW; 537 @Override onCreateDialog(Bundle savedInstanceState)538 public Dialog onCreateDialog(Bundle savedInstanceState) { 539 AppPermissionFragment fragment = (AppPermissionFragment) getParentFragment(); 540 boolean isGrantFileAccess = getArguments().getSerializable(CHANGE_REQUEST) 541 == ChangeRequest.GRANT_All_FILE_ACCESS; 542 int positiveButtonStringResId = R.string.grant_dialog_button_deny_anyway; 543 if (isGrantFileAccess) { 544 positiveButtonStringResId = R.string.grant_dialog_button_allow; 545 } 546 AlertDialog.Builder b = new AlertDialog.Builder(getContext()) 547 .setMessage(getArguments().getInt(MSG)) 548 .setNegativeButton(R.string.cancel, 549 (DialogInterface dialog, int which) -> dialog.cancel()) 550 .setPositiveButton(positiveButtonStringResId, 551 (DialogInterface dialog, int which) -> { 552 if (isGrantFileAccess) { 553 fragment.mViewModel.setAllFilesAccess(true); 554 fragment.mViewModel.requestChange(false, fragment, 555 fragment, ChangeRequest.GRANT_BOTH, sCode); 556 } else { 557 fragment.mViewModel.onDenyAnyWay((ChangeRequest) 558 getArguments().getSerializable(CHANGE_REQUEST), 559 getArguments().getInt(BUTTON), 560 getArguments().getBoolean(ONE_TIME)); 561 } 562 }); 563 Dialog d = b.create(); 564 d.setCanceledOnTouchOutside(true); 565 return d; 566 } 567 568 @Override onCancel(DialogInterface dialog)569 public void onCancel(DialogInterface dialog) { 570 AppPermissionFragment fragment = (AppPermissionFragment) getParentFragment(); 571 fragment.setRadioButtonsState(fragment.mViewModel.getButtonStateLiveData().getValue()); 572 } 573 } 574 } 575