/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ @file:Suppress("DEPRECATION") package com.android.permissioncontroller.permission.ui.handheld import android.Manifest.permission.ACCESS_COARSE_LOCATION import android.Manifest.permission.ACCESS_FINE_LOCATION import android.app.Activity import android.graphics.Typeface import android.graphics.drawable.Icon import android.os.Bundle import android.text.method.LinkMovementMethod import android.transition.ChangeBounds import android.transition.TransitionManager import android.util.SparseIntArray import android.view.Gravity import android.view.LayoutInflater import android.view.View import android.view.View.OnClickListener import android.view.ViewGroup import android.view.WindowManager.LayoutParams import android.view.accessibility.AccessibilityNodeInfo import android.view.animation.AnimationUtils import android.widget.Button import android.widget.ImageView import android.widget.LinearLayout import android.widget.RadioButton import android.widget.RadioGroup import android.widget.TextView import androidx.annotation.RawRes import com.airbnb.lottie.LottieCompositionFactory import com.airbnb.lottie.LottieDrawable import com.android.modules.utils.build.SdkLevel import com.android.permissioncontroller.R import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALL_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALWAYS_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_SELECTED_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.COARSE_RADIO_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DONT_ALLOW_MORE_SELECTED_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_LOCATION_DIALOG import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_MORE import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ONE_TIME import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_USER_SELECTED import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.ResultListener class GrantPermissionsViewHandlerImpl( private val mActivity: Activity, private val resultListener: ResultListener ) : GrantPermissionsViewHandler, OnClickListener { private val LOCATION_ACCURACY_DIALOGS = listOf( DIALOG_WITH_BOTH_LOCATIONS, DIALOG_WITH_FINE_LOCATION_ONLY, DIALOG_WITH_COARSE_LOCATION_ONLY ) private val LOCATION_ACCURACY_IMAGE_DIAMETER = mActivity.resources.getDimension(R.dimen.location_accuracy_image_size) // Configuration of the current dialog private var groupName: String? = null private var groupCount: Int = 0 private var groupIndex: Int = 0 private var groupIcon: Icon? = null private var groupMessage: CharSequence? = null private var detailMessage: CharSequence? = null private var permissionRationaleMessage: CharSequence? = null private val buttonVisibilities = BooleanArray(NEXT_BUTTON) { false } private val locationVisibilities = BooleanArray(NEXT_LOCATION_DIALOG) { false } private var selectedPrecision: Int = 0 private var isLocationPermissionDialogActionClicked: Boolean = false private var coarseRadioButton: RadioButton? = null private var fineRadioButton: RadioButton? = null private var coarseOffDrawable: LottieDrawable? = null private var coarseOnDrawable: LottieDrawable? = null private var fineOffDrawable: LottieDrawable? = null private var fineOnDrawable: LottieDrawable? = null // Views private var iconView: ImageView? = null private var messageView: TextView? = null private var detailMessageView: TextView? = null private var permissionRationaleView: View? = null private var permissionRationaleMessageView: TextView? = null private var buttons: Array = arrayOfNulls(NEXT_BUTTON) private var locationViews: Array = arrayOfNulls(NEXT_LOCATION_DIALOG) private var rootView: ViewGroup? = null override fun saveInstanceState(arguments: Bundle) { arguments.putString(ARG_GROUP_NAME, groupName) arguments.putInt(ARG_GROUP_COUNT, groupCount) arguments.putInt(ARG_GROUP_INDEX, groupIndex) arguments.putParcelable(ARG_GROUP_ICON, groupIcon) arguments.putCharSequence(ARG_GROUP_MESSAGE, groupMessage) arguments.putCharSequence(ARG_GROUP_DETAIL_MESSAGE, detailMessage) arguments.putCharSequence( ARG_GROUP_PERMISSION_RATIONALE_MESSAGE, permissionRationaleMessage ) arguments.putBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES, buttonVisibilities) arguments.putBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES, locationVisibilities) arguments.putInt(ARG_DIALOG_SELECTED_PRECISION, selectedPrecision) } override fun loadInstanceState(savedInstanceState: Bundle) { groupName = savedInstanceState.getString(ARG_GROUP_NAME) groupMessage = savedInstanceState.getCharSequence(ARG_GROUP_MESSAGE) groupIcon = savedInstanceState.getParcelable(ARG_GROUP_ICON) groupCount = savedInstanceState.getInt(ARG_GROUP_COUNT) groupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX) detailMessage = savedInstanceState.getCharSequence(ARG_GROUP_DETAIL_MESSAGE) permissionRationaleMessage = savedInstanceState.getCharSequence(ARG_GROUP_PERMISSION_RATIONALE_MESSAGE) setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES)) setLocationVisibilities( savedInstanceState.getBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES) ) selectedPrecision = savedInstanceState.getInt(ARG_DIALOG_SELECTED_PRECISION) updateAll() } override fun updateUi( groupName: String?, groupCount: Int, groupIndex: Int, icon: Icon?, message: CharSequence?, detailMessage: CharSequence?, permissionRationaleMessage: CharSequence?, buttonVisibilities: BooleanArray?, locationVisibilities: BooleanArray? ) { this.groupName = groupName this.groupCount = groupCount this.groupIndex = groupIndex groupIcon = icon groupMessage = message this.detailMessage = detailMessage this.permissionRationaleMessage = permissionRationaleMessage setButtonVisibilities(buttonVisibilities) setLocationVisibilities(locationVisibilities) // If this is a second (or later) permission and the views exist, then animate. if (iconView != null) { updateAll() } } private fun updateAll() { updateDescription() updateDetailDescription() updatePermissionRationale() updateButtons() updateLocationVisibilities() // Animate change in size // Grow or shrink the content container to size of new content val growShrinkToNewContentSize = ChangeBounds() growShrinkToNewContentSize.duration = ANIMATION_DURATION_MILLIS growShrinkToNewContentSize.interpolator = AnimationUtils.loadInterpolator(mActivity, android.R.interpolator.fast_out_slow_in) TransitionManager.beginDelayedTransition(rootView, growShrinkToNewContentSize) } override fun createView(): View { val useMaterial3PermissionGrantDialog = mActivity.resources.getBoolean(R.bool.config_useMaterial3PermissionGrantDialog) val rootView = if (useMaterial3PermissionGrantDialog || SdkLevel.isAtLeastT()) { LayoutInflater.from(mActivity).inflate(R.layout.grant_permissions_material3, null) as ViewGroup } else { LayoutInflater.from(mActivity).inflate(R.layout.grant_permissions, null) as ViewGroup } this.rootView = rootView // Uses the vertical gravity of the PermissionGrantSingleton style to position the window val gravity = rootView.requireViewById(R.id.grant_singleton).gravity val verticalGravity = Gravity.VERTICAL_GRAVITY_MASK and gravity mActivity.window.setGravity(Gravity.CENTER_HORIZONTAL or verticalGravity) // Cancel dialog rootView.findViewById(R.id.grant_singleton)!!.setOnClickListener(this) // Swallow click event rootView.findViewById(R.id.grant_dialog)!!.setOnClickListener(this) messageView = rootView.findViewById(R.id.permission_message) detailMessageView = rootView.findViewById(R.id.detail_message) detailMessageView!!.movementMethod = LinkMovementMethod.getInstance() iconView = rootView.findViewById(R.id.permission_icon) permissionRationaleView = rootView.findViewById(R.id.permission_rationale_container) permissionRationaleMessageView = rootView.findViewById(R.id.permission_rationale_message) permissionRationaleView!!.setOnClickListener(this) val buttons = arrayOfNulls