1 /* <lambda>null2 * Copyright (C) 2015 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 android.Manifest.permission.ACCESS_COARSE_LOCATION 20 import android.Manifest.permission.ACCESS_FINE_LOCATION 21 import android.app.Activity 22 import android.content.res.Configuration 23 import android.graphics.Color 24 import android.graphics.ImageDecoder 25 import android.graphics.Paint 26 import android.graphics.Path 27 import android.graphics.PixelFormat 28 import android.graphics.PorterDuff 29 import android.graphics.PorterDuffXfermode 30 import android.graphics.drawable.AnimatedImageDrawable 31 import android.graphics.drawable.Drawable 32 import android.graphics.drawable.Icon 33 import android.os.Bundle 34 import android.os.UserHandle 35 import android.text.method.LinkMovementMethod 36 import android.transition.ChangeBounds 37 import android.transition.TransitionManager 38 import android.util.SparseIntArray 39 import android.view.LayoutInflater 40 import android.view.View 41 import android.view.View.OnClickListener 42 import android.view.ViewGroup 43 import android.view.WindowManager.LayoutParams 44 import android.view.accessibility.AccessibilityNodeInfo 45 import android.view.animation.AnimationUtils 46 import android.widget.Button 47 import android.widget.ImageView 48 import android.widget.RadioButton 49 import android.widget.RadioGroup 50 import android.widget.TextView 51 import com.android.permissioncontroller.R 52 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALWAYS_BUTTON 53 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON 54 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON 55 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON 56 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.COARSE_RADIO_BUTTON 57 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON 58 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON 59 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS 60 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY 61 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY 62 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON 63 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT 64 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON 65 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_LOCATION_DIALOG 66 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON 67 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON 68 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON 69 import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON 70 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler 71 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED 72 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED 73 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN 74 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS 75 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY 76 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ONE_TIME 77 78 class GrantPermissionsViewHandlerImpl( 79 private val mActivity: Activity, 80 private val mAppPackageName: String, 81 private val mUserHandle: UserHandle 82 ) : GrantPermissionsViewHandler, OnClickListener { 83 84 private val LOCATION_ACCURACY_DIALOGS = listOf(DIALOG_WITH_BOTH_LOCATIONS, 85 DIALOG_WITH_FINE_LOCATION_ONLY, DIALOG_WITH_COARSE_LOCATION_ONLY) 86 private val LOCATION_ACCURACY_IMAGE_DIAMETER = mActivity.resources.getDimension( 87 R.dimen.location_accuracy_image_size).toInt() 88 89 private var resultListener: GrantPermissionsViewHandler.ResultListener? = null 90 91 // Configuration of the current dialog 92 private var groupName: String? = null 93 private var groupCount: Int = 0 94 private var groupIndex: Int = 0 95 private var groupIcon: Icon? = null 96 private var groupMessage: CharSequence? = null 97 private var detailMessage: CharSequence? = null 98 private val buttonVisibilities = BooleanArray(NEXT_BUTTON) { false } 99 private val locationVisibilities = BooleanArray(NEXT_LOCATION_DIALOG) { false } 100 private var selectedPrecision: Int = 0 101 private var isLocationPermissionDialogActionClicked: Boolean = false 102 private var coarseRadioButton: RadioButton? = null 103 private var fineRadioButton: RadioButton? = null 104 private var coarseOffDrawable: AnimatedImageDrawable? = null 105 private var coarseOnDrawable: AnimatedImageDrawable? = null 106 private var fineOffDrawable: AnimatedImageDrawable? = null 107 private var fineOnDrawable: AnimatedImageDrawable? = null 108 109 // Views 110 private var iconView: ImageView? = null 111 private var messageView: TextView? = null 112 private var detailMessageView: TextView? = null 113 private var buttons: Array<Button?> = arrayOfNulls(NEXT_BUTTON) 114 private var locationViews: Array<View?> = arrayOfNulls(NEXT_LOCATION_DIALOG) 115 private var rootView: ViewGroup? = null 116 117 override fun setResultListener( 118 listener: GrantPermissionsViewHandler.ResultListener 119 ): GrantPermissionsViewHandlerImpl { 120 resultListener = listener 121 return this 122 } 123 124 override fun saveInstanceState(arguments: Bundle) { 125 arguments.putString(ARG_GROUP_NAME, groupName) 126 arguments.putInt(ARG_GROUP_COUNT, groupCount) 127 arguments.putInt(ARG_GROUP_INDEX, groupIndex) 128 arguments.putParcelable(ARG_GROUP_ICON, groupIcon) 129 arguments.putCharSequence(ARG_GROUP_MESSAGE, groupMessage) 130 arguments.putCharSequence(ARG_GROUP_DETAIL_MESSAGE, detailMessage) 131 arguments.putBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES, buttonVisibilities) 132 arguments.putBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES, locationVisibilities) 133 arguments.putInt(ARG_DIALOG_SELECTED_PRECISION, selectedPrecision) 134 } 135 136 override fun loadInstanceState(savedInstanceState: Bundle) { 137 groupName = savedInstanceState.getString(ARG_GROUP_NAME) 138 groupMessage = savedInstanceState.getCharSequence(ARG_GROUP_MESSAGE) 139 groupIcon = savedInstanceState.getParcelable(ARG_GROUP_ICON) 140 groupCount = savedInstanceState.getInt(ARG_GROUP_COUNT) 141 groupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX) 142 detailMessage = savedInstanceState.getCharSequence(ARG_GROUP_DETAIL_MESSAGE) 143 setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES)) 144 setLocationVisibilities(savedInstanceState.getBooleanArray( 145 ARG_DIALOG_LOCATION_VISIBILITIES)) 146 selectedPrecision = savedInstanceState.getInt(ARG_DIALOG_SELECTED_PRECISION) 147 148 updateAll() 149 } 150 151 override fun updateUi( 152 groupName: String, 153 groupCount: Int, 154 groupIndex: Int, 155 icon: Icon?, 156 message: CharSequence?, 157 detailMessage: CharSequence?, 158 buttonVisibilities: BooleanArray, 159 locationVisibilities: BooleanArray 160 ) { 161 162 this.groupName = groupName 163 this.groupCount = groupCount 164 this.groupIndex = groupIndex 165 groupIcon = icon 166 groupMessage = message 167 this.detailMessage = detailMessage 168 setButtonVisibilities(buttonVisibilities) 169 setLocationVisibilities(locationVisibilities) 170 171 // If this is a second (or later) permission and the views exist, then animate. 172 if (iconView != null) { 173 updateAll() 174 } 175 } 176 177 private fun updateAll() { 178 updateDescription() 179 updateDetailDescription() 180 updateButtons() 181 updateLocationVisibilities() 182 183 // Animate change in size 184 // Grow or shrink the content container to size of new content 185 val growShrinkToNewContentSize = ChangeBounds() 186 growShrinkToNewContentSize.duration = ANIMATION_DURATION_MILLIS 187 growShrinkToNewContentSize.interpolator = AnimationUtils.loadInterpolator(mActivity, 188 android.R.interpolator.fast_out_slow_in) 189 TransitionManager.beginDelayedTransition(rootView, growShrinkToNewContentSize) 190 } 191 192 override fun createView(): View { 193 // Make this activity be Non-IME target to prevent hiding keyboard flicker when it show up. 194 mActivity.window.addFlags(LayoutParams.FLAG_ALT_FOCUSABLE_IM) 195 val rootView = LayoutInflater.from(mActivity) 196 .inflate(R.layout.grant_permissions, null) as ViewGroup 197 this.rootView = rootView 198 199 val h = mActivity.resources.displayMetrics.heightPixels 200 rootView.minimumHeight = h 201 // Cancel dialog 202 rootView.findViewById<View>(R.id.grant_singleton)!!.setOnClickListener(this) 203 // Swallow click event 204 rootView.findViewById<View>(R.id.grant_dialog)!!.setOnClickListener(this) 205 206 messageView = rootView.findViewById(R.id.permission_message) 207 detailMessageView = rootView.findViewById(R.id.detail_message) 208 detailMessageView!!.movementMethod = LinkMovementMethod.getInstance() 209 iconView = rootView.findViewById(R.id.permission_icon) 210 211 val buttons = arrayOfNulls<Button>(NEXT_BUTTON) 212 val numButtons = BUTTON_RES_ID_TO_NUM.size() 213 for (i in 0 until numButtons) { 214 val button = rootView.findViewById<Button>(BUTTON_RES_ID_TO_NUM.keyAt(i)) 215 button!!.setOnClickListener(this) 216 buttons[BUTTON_RES_ID_TO_NUM.valueAt(i)] = button 217 } 218 this.buttons = buttons 219 220 val locationViews = arrayOfNulls<View>(NEXT_LOCATION_DIALOG) 221 for (i in 0 until LOCATION_RES_ID_TO_NUM.size()) { 222 val locationView = rootView.findViewById<View>(LOCATION_RES_ID_TO_NUM.keyAt(i)) 223 locationViews[LOCATION_RES_ID_TO_NUM.valueAt(i)] = locationView 224 } 225 initializeAnimatedImages() 226 227 // Set location accuracy radio buttons' click listeners 228 coarseRadioButton = locationViews[COARSE_RADIO_BUTTON] as RadioButton 229 fineRadioButton = locationViews[FINE_RADIO_BUTTON] as RadioButton 230 coarseRadioButton!!.setOnClickListener(this) 231 fineRadioButton!!.setOnClickListener(this) 232 this.locationViews = locationViews 233 234 if (groupName != null) { 235 updateAll() 236 } 237 238 return rootView 239 } 240 241 private fun initializeAnimatedImages() { 242 val isDarkMode = (mActivity.resources.configuration.uiMode and 243 Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES 244 val coarseOffDrawableId = if (isDarkMode) R.drawable.coarse_off_dark 245 else R.drawable.coarse_off_light 246 val coarseOnDrawableId = if (isDarkMode) R.drawable.coarse_on_dark 247 else R.drawable.coarse_on_light 248 val fineOffDrawableId = if (isDarkMode) R.drawable.fine_off_dark 249 else R.drawable.fine_off_light 250 val fineOnDrawableId = if (isDarkMode) R.drawable.fine_on_dark else R.drawable.fine_on_light 251 252 coarseOffDrawable = getDrawableFromId(coarseOffDrawableId) as AnimatedImageDrawable 253 coarseOnDrawable = getDrawableFromId(coarseOnDrawableId) as AnimatedImageDrawable 254 fineOffDrawable = getDrawableFromId(fineOffDrawableId) as AnimatedImageDrawable 255 fineOnDrawable = getDrawableFromId(fineOnDrawableId) as AnimatedImageDrawable 256 } 257 258 private fun getDrawableFromId(drawableId: Int): Drawable { 259 val source = ImageDecoder.createSource(mActivity.resources, drawableId) 260 return ImageDecoder.decodeDrawable(source) { decoder, _, _ -> 261 decoder.setTargetSize(LOCATION_ACCURACY_IMAGE_DIAMETER, 262 LOCATION_ACCURACY_IMAGE_DIAMETER) 263 decoder.setPostProcessor { canvas -> 264 // This will crop the image to circle image. 265 val path = Path() 266 path.fillType = Path.FillType.INVERSE_EVEN_ODD 267 val width: Int = canvas.width 268 val height: Int = canvas.height 269 path.addRoundRect(0f, 0f, width.toFloat(), height.toFloat(), 270 width.toFloat() / 2, height.toFloat() / 2, Path.Direction.CW) 271 val paint = Paint() 272 paint.isAntiAlias = true 273 paint.color = Color.TRANSPARENT 274 paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC) 275 canvas.drawPath(path, paint) 276 PixelFormat.TRANSLUCENT 277 } 278 } 279 } 280 281 override fun updateWindowAttributes(outLayoutParams: LayoutParams) { 282 // No-op 283 } 284 285 private fun setButtonVisibilities(visibilities: BooleanArray?) { 286 for (i in buttonVisibilities.indices) { 287 buttonVisibilities[i] = if (visibilities != null && i < visibilities.size) { 288 visibilities[i] 289 } else { 290 false 291 } 292 } 293 } 294 295 private fun setLocationVisibilities(visibilities: BooleanArray?) { 296 for (i in locationVisibilities.indices) { 297 locationVisibilities[i] = if (visibilities != null && i < visibilities.size) { 298 visibilities[i] 299 } else { 300 false 301 } 302 } 303 } 304 305 private fun updateDescription() { 306 if (groupIcon != null) { 307 iconView!!.setImageDrawable(groupIcon!!.loadDrawable(mActivity)) 308 } 309 messageView!!.text = groupMessage 310 } 311 312 private fun updateDetailDescription() { 313 if (detailMessage == null) { 314 detailMessageView!!.visibility = View.GONE 315 } else { 316 detailMessageView!!.text = detailMessage 317 detailMessageView!!.visibility = View.VISIBLE 318 } 319 } 320 321 private fun updateButtons() { 322 for (i in 0 until BUTTON_RES_ID_TO_NUM.size()) { 323 val pos = BUTTON_RES_ID_TO_NUM.valueAt(i) 324 buttons[pos]?.visibility = if (buttonVisibilities[pos]) { 325 View.VISIBLE 326 } else { 327 View.GONE 328 } 329 if (pos == ALLOW_FOREGROUND_BUTTON && buttonVisibilities[pos] && 330 locationVisibilities[LOCATION_ACCURACY_LAYOUT] && 331 locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) { 332 buttons[pos]?.text = mActivity.resources.getString( 333 R.string.grant_dialog_button_change_to_precise_location) 334 } 335 if ((pos == DENY_BUTTON || pos == DENY_AND_DONT_ASK_AGAIN_BUTTON) && 336 locationVisibilities[LOCATION_ACCURACY_LAYOUT] && 337 locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) { 338 buttons[pos]?.text = mActivity.resources.getString( 339 R.string.grant_dialog_button_keey_approximate_location) 340 } 341 buttons[pos]?.requestLayout() 342 } 343 } 344 345 private fun updateLocationVisibilities() { 346 if (locationVisibilities[LOCATION_ACCURACY_LAYOUT]) { 347 if (isLocationPermissionDialogActionClicked) { 348 return 349 } 350 locationViews[LOCATION_ACCURACY_LAYOUT]?.visibility = View.VISIBLE 351 for (i in LOCATION_ACCURACY_DIALOGS) { 352 locationViews[i]?.visibility = if (locationVisibilities[i]) { 353 View.VISIBLE 354 } else { 355 View.GONE 356 } 357 } 358 if (locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) { 359 coarseRadioButton?.visibility = View.VISIBLE 360 fineRadioButton?.visibility = View.VISIBLE 361 if (selectedPrecision == 0) { 362 fineRadioButton?.isChecked = locationVisibilities[FINE_RADIO_BUTTON] 363 coarseRadioButton?.isChecked = locationVisibilities[COARSE_RADIO_BUTTON] 364 } else { 365 fineRadioButton?.isChecked = selectedPrecision == FINE_RADIO_BUTTON 366 coarseRadioButton?.isChecked = selectedPrecision == COARSE_RADIO_BUTTON 367 } 368 if (coarseRadioButton?.isChecked == true) { 369 runLocationAccuracyAnimation(false) 370 } else if (fineRadioButton?.isChecked == true) { 371 runLocationAccuracyAnimation(true) 372 } 373 } else if (locationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) { 374 (locationViews[DIALOG_WITH_COARSE_LOCATION_ONLY] as ImageView).setImageDrawable( 375 coarseOnDrawable) 376 coarseOnDrawable?.start() 377 } else if (locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) { 378 (locationViews[DIALOG_WITH_FINE_LOCATION_ONLY] as ImageView).setImageDrawable( 379 fineOnDrawable) 380 fineOnDrawable?.start() 381 } 382 } else { 383 locationViews[LOCATION_ACCURACY_LAYOUT]?.visibility = View.GONE 384 for (i in LOCATION_ACCURACY_DIALOGS) { 385 locationVisibilities[i] = false 386 locationViews[i]?.visibility = View.GONE 387 } 388 } 389 } 390 391 private fun runLocationAccuracyAnimation(isFineSelected: Boolean) { 392 if (isFineSelected) { 393 coarseOnDrawable?.stop() 394 fineOffDrawable?.stop() 395 coarseRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, coarseOffDrawable, 396 null, null) 397 fineRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, fineOnDrawable, 398 null, null) 399 coarseOffDrawable?.start() 400 fineOnDrawable?.start() 401 } else { 402 coarseOffDrawable?.stop() 403 fineOnDrawable?.stop() 404 coarseRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, coarseOnDrawable, 405 null, null) 406 fineRadioButton?.setCompoundDrawablesWithIntrinsicBounds(null, fineOffDrawable, 407 null, null) 408 coarseOnDrawable?.start() 409 fineOffDrawable?.start() 410 } 411 } 412 413 override fun onClick(view: View) { 414 val id = view.id 415 416 if (id == R.id.permission_location_accuracy_radio_fine) { 417 (locationViews[FINE_RADIO_BUTTON] as RadioButton).isChecked = true 418 selectedPrecision = FINE_RADIO_BUTTON 419 runLocationAccuracyAnimation(true) 420 return 421 } 422 423 if (id == R.id.permission_location_accuracy_radio_coarse) { 424 (locationViews[COARSE_RADIO_BUTTON] as RadioButton).isChecked = true 425 selectedPrecision = COARSE_RADIO_BUTTON 426 runLocationAccuracyAnimation(false) 427 return 428 } 429 430 if (locationVisibilities[LOCATION_ACCURACY_LAYOUT]) { 431 isLocationPermissionDialogActionClicked = true 432 } 433 434 if (id == R.id.grant_singleton) { 435 if (resultListener != null) { 436 resultListener!!.onPermissionGrantResult(groupName, CANCELED) 437 } else { 438 mActivity.finishAfterTransition() 439 } 440 return 441 } 442 443 var affectedForegroundPermissions: List<String>? = null 444 if (locationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) { 445 when ((locationViews[DIALOG_WITH_BOTH_LOCATIONS] as RadioGroup).checkedRadioButtonId) { 446 R.id.permission_location_accuracy_radio_coarse -> 447 affectedForegroundPermissions = listOf(ACCESS_COARSE_LOCATION) 448 R.id.permission_location_accuracy_radio_fine -> 449 affectedForegroundPermissions = listOf(ACCESS_FINE_LOCATION, 450 ACCESS_COARSE_LOCATION) 451 } 452 } else if (locationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) { 453 affectedForegroundPermissions = listOf(ACCESS_FINE_LOCATION) 454 } else if (locationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) { 455 affectedForegroundPermissions = listOf(ACCESS_COARSE_LOCATION) 456 } 457 458 when (BUTTON_RES_ID_TO_NUM.get(id, -1)) { 459 ALLOW_BUTTON -> if (resultListener != null) { 460 view.performAccessibilityAction( 461 AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) 462 resultListener!!.onPermissionGrantResult(groupName, affectedForegroundPermissions, 463 GRANTED_ALWAYS) 464 } 465 ALLOW_FOREGROUND_BUTTON -> if (resultListener != null) { 466 view.performAccessibilityAction( 467 AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) 468 resultListener!!.onPermissionGrantResult(groupName, affectedForegroundPermissions, 469 GRANTED_FOREGROUND_ONLY) 470 } 471 ALLOW_ALWAYS_BUTTON -> if (resultListener != null) { 472 view.performAccessibilityAction( 473 AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) 474 resultListener!!.onPermissionGrantResult(groupName, affectedForegroundPermissions, 475 GRANTED_ALWAYS) 476 } 477 ALLOW_ONE_TIME_BUTTON -> if (resultListener != null) { 478 view.performAccessibilityAction( 479 AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) 480 resultListener!!.onPermissionGrantResult(groupName, affectedForegroundPermissions, 481 GRANTED_ONE_TIME) 482 } 483 DENY_BUTTON, NO_UPGRADE_BUTTON, NO_UPGRADE_OT_BUTTON -> if (resultListener != null) { 484 view.performAccessibilityAction( 485 AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) 486 resultListener!!.onPermissionGrantResult(groupName, affectedForegroundPermissions, 487 DENIED) 488 } 489 DENY_AND_DONT_ASK_AGAIN_BUTTON, NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON, 490 NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON -> if (resultListener != null) { 491 view.performAccessibilityAction( 492 AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null) 493 resultListener!!.onPermissionGrantResult(groupName, affectedForegroundPermissions, 494 DENIED_DO_NOT_ASK_AGAIN) 495 } 496 } 497 } 498 499 override fun onBackPressed() { 500 if (resultListener == null) { 501 mActivity.finishAfterTransition() 502 return 503 } 504 resultListener?.onPermissionGrantResult(groupName, CANCELED) 505 } 506 507 companion object { 508 509 const val ARG_GROUP_NAME = "ARG_GROUP_NAME" 510 const val ARG_GROUP_COUNT = "ARG_GROUP_COUNT" 511 const val ARG_GROUP_INDEX = "ARG_GROUP_INDEX" 512 const val ARG_GROUP_ICON = "ARG_GROUP_ICON" 513 const val ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE" 514 private const val ARG_GROUP_DETAIL_MESSAGE = "ARG_GROUP_DETAIL_MESSAGE" 515 private const val ARG_DIALOG_BUTTON_VISIBILITIES = "ARG_DIALOG_BUTTON_VISIBILITIES" 516 private const val ARG_DIALOG_LOCATION_VISIBILITIES = "ARG_DIALOG_LOCATION_VISIBILITIES" 517 private const val ARG_DIALOG_SELECTED_PRECISION = "ARG_DIALOG_SELECTED_PRECISION" 518 519 // Animation parameters. 520 private const val SWITCH_TIME_MILLIS: Long = 75 521 private const val ANIMATION_DURATION_MILLIS: Long = 200 522 523 private val BUTTON_RES_ID_TO_NUM = SparseIntArray() 524 private val LOCATION_RES_ID_TO_NUM = SparseIntArray() 525 526 init { 527 BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_button, ALLOW_BUTTON) 528 BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_foreground_only_button, 529 ALLOW_FOREGROUND_BUTTON) 530 BUTTON_RES_ID_TO_NUM.put(R.id.permission_deny_button, DENY_BUTTON) 531 BUTTON_RES_ID_TO_NUM.put(R.id.permission_deny_and_dont_ask_again_button, 532 DENY_AND_DONT_ASK_AGAIN_BUTTON) 533 BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_one_time_button, 534 ALLOW_ONE_TIME_BUTTON) 535 BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_button, 536 NO_UPGRADE_BUTTON) 537 BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_and_dont_ask_again_button, 538 NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON) 539 BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_button, 540 NO_UPGRADE_OT_BUTTON) 541 BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_and_dont_ask_again_button, 542 NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON) 543 544 LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy, LOCATION_ACCURACY_LAYOUT) 545 LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_radio_fine, 546 FINE_RADIO_BUTTON) 547 LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_radio_coarse, 548 COARSE_RADIO_BUTTON) 549 LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_radio_group, 550 DIALOG_WITH_BOTH_LOCATIONS) 551 LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_fine_only, 552 DIALOG_WITH_FINE_LOCATION_ONLY) 553 LOCATION_RES_ID_TO_NUM.put(R.id.permission_location_accuracy_coarse_only, 554 DIALOG_WITH_COARSE_LOCATION_ONLY) 555 } 556 } 557 } 558