• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 android.Manifest.permission.ACCESS_COARSE_LOCATION;
20 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
21 
22 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALL_BUTTON;
23 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ALWAYS_BUTTON;
24 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON;
25 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON;
26 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON;
27 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_SELECTED_BUTTON;
28 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.COARSE_RADIO_BUTTON;
29 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON;
30 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON;
31 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS;
32 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY;
33 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY;
34 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DONT_ALLOW_MORE_SELECTED_BUTTON;
35 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON;
36 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.LOCATION_ACCURACY_LAYOUT;
37 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON;
38 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_LOCATION_DIALOG;
39 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON;
40 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON;
41 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON;
42 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON;
43 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.CANCELED;
44 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED;
45 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_DO_NOT_ASK_AGAIN;
46 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.DENIED_MORE;
47 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS;
48 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_FOREGROUND_ONLY;
49 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_ONE_TIME;
50 import static com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler.GRANTED_USER_SELECTED;
51 
52 import android.app.Activity;
53 import android.content.Context;
54 import android.graphics.Typeface;
55 import android.graphics.drawable.Icon;
56 import android.text.method.LinkMovementMethod;
57 import android.transition.ChangeBounds;
58 import android.transition.TransitionManager;
59 import android.util.SparseIntArray;
60 import android.view.Gravity;
61 import android.view.LayoutInflater;
62 import android.view.View;
63 import android.view.ViewGroup;
64 import android.view.Window;
65 import android.view.accessibility.AccessibilityNodeInfo;
66 import android.view.animation.AnimationUtils;
67 import android.widget.Button;
68 import android.widget.ImageView;
69 import android.widget.RadioButton;
70 import android.widget.RadioGroup;
71 import android.widget.TextView;
72 
73 import androidx.annotation.RawRes;
74 
75 import com.android.permissioncontroller.R;
76 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler;
77 
78 import com.airbnb.lottie.LottieComposition;
79 import com.airbnb.lottie.LottieCompositionFactory;
80 import com.airbnb.lottie.LottieDrawable;
81 
82 import java.util.ArrayList;
83 import java.util.HashMap;
84 import java.util.List;
85 import java.util.Map;
86 
87 /**
88  * An extension of {@link GrantPermissionsAutoViewHandler} that updates the current auto location
89  * permission prompt with a custom view
90  */
91 public class AutoLocationPermissionPromptView implements View.OnClickListener {
92     private static final int[] LOCATION_ACCURACY_DIALOGS =
93     {
94         DIALOG_WITH_BOTH_LOCATIONS,
95         DIALOG_WITH_FINE_LOCATION_ONLY,
96         DIALOG_WITH_COARSE_LOCATION_ONLY
97     };
98     private static final long ANIMATION_DURATION_MILLIS = 200;
99     private static final float LOCATION_IMAGE_SIZE_MODIFIER = 2;
100     private final Context mContext;
101     private GrantPermissionsViewHandler.ResultListener mResultListener;
102     private String mGroupName;
103     private Icon mGroupIcon;
104     private CharSequence mGroupMessage;
105     private CharSequence mDetailMessage;
106     private boolean[] mButtonVisibilities;
107     private boolean[] mLocationVisibilities;
108     private SparseIntArray mButtonResIdToNum = new SparseIntArray();
109     private SparseIntArray mLocationResIdToNum = new SparseIntArray();
110     private boolean mIsLocationPermissionDialogActionClicked = false;
111     private LottieDrawable mCoarseOffDrawable = null;
112     private LottieDrawable mCoarseOnDrawable = null;
113     private LottieDrawable mFineOffDrawable = null;
114     private LottieDrawable mFineOnDrawable = null;
115 
116     // Views
117     private ViewGroup mRootView = null;
118     private TextView mMessageView = null;
119     private TextView mDetailedMessageView = null;
120     private Button[] mButtons = new Button[NEXT_BUTTON];
121     private View[] mLocationViews = new View[NEXT_LOCATION_DIALOG];
122 
123     // Dialog Config
124     private RadioButton mCoarseRadioButton = null;
125     private RadioButton mFineRadioButton = null;
126     private int mSelectedPrecision = 0;
127     private float mLocationAccuracyImageDiameter = 0;
128 
129     /**
130      * Constructs a new AutoLocationPermissionPromptView.
131      *
132      * @param context the Context in which the view is created
133      */
AutoLocationPermissionPromptView(Context context)134     public AutoLocationPermissionPromptView(Context context) {
135         mContext = context;
136         generateResIdToButtonMap();
137         mLocationAccuracyImageDiameter =
138             mContext.getResources().getDimension(R.dimen.location_accuracy_image_size)
139                     + LOCATION_IMAGE_SIZE_MODIFIER;
140     }
141 
142     /**
143      * Sets the ResultListener for this view. The ResultListener will be notified when
144      * the user makes a selection in the permission prompt.
145      *
146      * @param listener the ResultListener to set
147      */
setResultListener(GrantPermissionsViewHandler.ResultListener listener)148     public void setResultListener(GrantPermissionsViewHandler.ResultListener listener) {
149         mResultListener = listener;
150     }
151 
152     /**
153      * Creates and initializes the custom view for the auto location permission prompt.
154      * This method inflates the layout, sets up various UI elements, and configures their behaviors
155      * such as click listeners and gravity adjustments.
156      */
createView()157     public View createView() {
158         ViewGroup rootView = (ViewGroup) LayoutInflater.from(mContext).inflate(
159                 R.layout.auto_grant_permissions_material3, null);
160 
161         this.mRootView = rootView;
162 
163         int gravity = rootView.requireViewById(R.id.grant_singleton).getForegroundGravity();
164         int verticalGravity = Gravity.VERTICAL_GRAVITY_MASK & gravity;
165         if (mContext instanceof Activity activity) {
166             Window window = activity.getWindow();
167             window.setGravity(Gravity.CENTER_HORIZONTAL | verticalGravity);
168         }
169 
170         rootView.requireViewById(R.id.grant_singleton).setOnClickListener(this);
171         rootView.requireViewById(R.id.grant_dialog).setOnClickListener(this);
172 
173         mMessageView = rootView.requireViewById(R.id.permission_message);
174         mDetailedMessageView = rootView.requireViewById(R.id.detail_message);
175         mDetailedMessageView.setMovementMethod(LinkMovementMethod.getInstance());
176 
177         Button[] buttons = new Button[NEXT_BUTTON];
178         int numButtons = mButtonResIdToNum.size();
179         for (int i = 0; i < numButtons; i++) {
180             Button button = rootView.requireViewById(mButtonResIdToNum.keyAt(i));
181             button.setOnClickListener(this);
182             buttons[mButtonResIdToNum.valueAt(i)] = button;
183         }
184         this.mButtons = buttons;
185 
186         View[] locationViews = new View[NEXT_LOCATION_DIALOG];
187         for (int i = 0; i < mLocationResIdToNum.size(); i++) {
188             View locationView = rootView.requireViewById(mLocationResIdToNum.keyAt(i));
189             locationViews[mLocationResIdToNum.valueAt(i)] = locationView;
190         }
191 
192         initializeAnimatedImages();
193 
194         mCoarseRadioButton = (RadioButton) locationViews[COARSE_RADIO_BUTTON];
195         mFineRadioButton = (RadioButton) locationViews[FINE_RADIO_BUTTON];
196         mCoarseRadioButton.setOnClickListener(this);
197         mFineRadioButton.setOnClickListener(this);
198         this.mLocationViews = locationViews;
199 
200         if (mDetailMessage != null) {
201             updateAll();
202         }
203         return rootView;
204     }
205 
initializeAnimatedImages()206     private void initializeAnimatedImages() {
207         mCoarseOffDrawable = getLottieDrawable(R.raw.coarse_loc_off);
208         mCoarseOnDrawable = getLottieDrawable(R.raw.coarse_loc_on);
209         mFineOffDrawable = getLottieDrawable(R.raw.fine_loc_off);
210         mFineOnDrawable = getLottieDrawable(R.raw.fine_loc_on);
211     }
212 
getLottieDrawable(@awRes int rawResId)213     private LottieDrawable getLottieDrawable(@RawRes int rawResId) {
214         LottieComposition composition = LottieCompositionFactory
215                 .fromRawResSync(mContext, rawResId).getValue();
216         if (composition == null) {
217             throw new NullPointerException();
218         }
219 
220         float scale = mLocationAccuracyImageDiameter / composition.getBounds().width();
221         LottieDrawable drawable = new LottieDrawable() {
222             @Override
223             public int getIntrinsicHeight() {
224                 return ((int) (super.getIntrinsicHeight() * scale));
225             }
226 
227             @Override
228             public int getIntrinsicWidth() {
229                 return ((int) (super.getIntrinsicWidth() * scale));
230             }
231         };
232         drawable.setComposition(composition);
233         return drawable;
234     }
235 
236     /**
237      * Updates the state of the view with the provided parameters. This method is called to refresh
238      * the UI elements with new data, ensuring that the view reflects the latest information.
239      */
updateState(String groupName, Icon icon, CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities, boolean[] locationVisibilities, int selectedPrecision)240     public void updateState(String groupName, Icon icon,
241             CharSequence message, CharSequence detailMessage, boolean[] buttonVisibilities,
242             boolean[] locationVisibilities, int selectedPrecision) {
243 
244         mGroupName = groupName;
245         mGroupIcon = icon;
246         mGroupMessage = message;
247         mDetailMessage = detailMessage;
248         mSelectedPrecision = selectedPrecision;
249         setButtonVisibilities(buttonVisibilities);
250         setLocationVisibilities(locationVisibilities);
251 
252         // If the iconView is null, it's likely other related views are also null. Attempting to
253         // update variables in this state could lead to a null object reference and crash the app.
254         if (mMessageView != null) {
255             updateAll();
256         }
257     }
258 
updateAll()259     private void updateAll() {
260         updateDescription();
261         updateDetailDescription();
262         updateButtons();
263         updateLocationVisibilities();
264 
265         ChangeBounds growShrinkToNewContentSize = new ChangeBounds();
266         growShrinkToNewContentSize.setDuration(ANIMATION_DURATION_MILLIS);
267         growShrinkToNewContentSize.setInterpolator(
268                 AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in));
269         TransitionManager.beginDelayedTransition(mRootView, growShrinkToNewContentSize);
270     }
271 
updateLocationVisibilities()272     private void updateLocationVisibilities() {
273         if (mLocationVisibilities[LOCATION_ACCURACY_LAYOUT]) {
274             if (mIsLocationPermissionDialogActionClicked) {
275                 return;
276             }
277             mLocationViews[LOCATION_ACCURACY_LAYOUT].setVisibility(View.VISIBLE);
278             for (int i : LOCATION_ACCURACY_DIALOGS) {
279                 if (mLocationVisibilities[i]) {
280                     mLocationViews[i].setVisibility(View.VISIBLE);
281                 } else {
282                     mLocationViews[i].setVisibility(View.GONE);
283                 }
284             }
285 
286             if (mLocationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) {
287                 mCoarseRadioButton.setVisibility(View.VISIBLE);
288                 mFineRadioButton.setVisibility(View.VISIBLE);
289                 if (mSelectedPrecision == 0) {
290                     mFineRadioButton.setChecked(mLocationVisibilities[FINE_RADIO_BUTTON]);
291                     mCoarseRadioButton.setChecked(mLocationVisibilities[COARSE_RADIO_BUTTON]);
292                 } else {
293                     mFineRadioButton.setChecked(mSelectedPrecision == FINE_RADIO_BUTTON);
294                     mCoarseRadioButton.setChecked(mSelectedPrecision == COARSE_RADIO_BUTTON);
295                 }
296                 if (mCoarseRadioButton.isChecked()) {
297                     runLocationAccuracyAnimation(false);
298 
299                 } else if (mFineRadioButton.isChecked()) {
300                     runLocationAccuracyAnimation(true);
301                 }
302             } else if (mLocationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) {
303                 ((ImageView) mLocationViews[DIALOG_WITH_COARSE_LOCATION_ONLY])
304                         .setImageDrawable(mCoarseOnDrawable);
305                 mCoarseOnDrawable.start();
306             } else if (mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) {
307                 ((ImageView) mLocationViews[DIALOG_WITH_FINE_LOCATION_ONLY])
308                         .setImageDrawable(mFineOnDrawable);
309                 mFineOnDrawable.start();
310             }
311         } else {
312             mLocationViews[LOCATION_ACCURACY_LAYOUT].setVisibility(View.GONE);
313             for (int i : LOCATION_ACCURACY_DIALOGS) {
314                 mLocationVisibilities[i] = false;
315                 mLocationViews[i].setVisibility(View.GONE);
316             }
317         }
318     }
319 
runLocationAccuracyAnimation(boolean isFineSelected)320     private void runLocationAccuracyAnimation(boolean isFineSelected) {
321         if (isFineSelected) {
322             mCoarseOnDrawable.stop();
323             mFineOffDrawable.stop();
324             // Sets the drawable to appear to the left, top, right, or bottom of the text
325             mCoarseRadioButton.setCompoundDrawablesWithIntrinsicBounds(
326                     /* left */ null,
327                     /* top */ mCoarseOffDrawable,
328                     /* right */ null,
329                     /* bottom */ null
330             );
331             mFineRadioButton.setCompoundDrawablesWithIntrinsicBounds(
332                     /* left */ null,
333                     /* top */ mFineOnDrawable,
334                     /* right */ null,
335                     /* bottom */ null
336             );
337             mCoarseOffDrawable.start();
338             mFineOnDrawable.start();
339             mFineRadioButton.setTypeface(null, Typeface.BOLD);
340             mCoarseRadioButton.setTypeface(null, Typeface.NORMAL);
341         } else {
342             mCoarseOffDrawable.stop();
343             mFineOnDrawable.stop();
344             mCoarseRadioButton.setCompoundDrawablesWithIntrinsicBounds(
345                     /* left */ null,
346                     /* top */ mCoarseOnDrawable,
347                     /* right */ null,
348                     /* bottom */ null
349             );
350             mFineRadioButton.setCompoundDrawablesWithIntrinsicBounds(
351                     /* left */ null,
352                     /* top */ mFineOffDrawable,
353                     /* right */ null,
354                     /* bottom */ null
355             );
356             mFineOffDrawable.start();
357             mCoarseOnDrawable.start();
358             mCoarseRadioButton.setTypeface(null, Typeface.BOLD);
359             mFineRadioButton.setTypeface(null, Typeface.NORMAL);
360         }
361     }
362 
updateButtons()363     private void updateButtons() {
364         for (int i = 0; i < mButtonResIdToNum.size(); i++) {
365             int pos = mButtonResIdToNum.valueAt(i);
366             if (pos < mButtonVisibilities.length && mButtonVisibilities[pos]) {
367                 mButtons[pos].setVisibility(View.VISIBLE);
368             } else {
369                 mButtons[pos].setVisibility(View.GONE);
370             }
371             if (pos == ALLOW_FOREGROUND_BUTTON && mButtonVisibilities[pos]) {
372                 if (mLocationVisibilities[LOCATION_ACCURACY_LAYOUT]
373                         && mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]
374                 ) {
375                     mButtons[pos].setText(mContext.getResources().getString(
376                             R.string.grant_dialog_button_change_to_precise_location));
377                 } else {
378                     mButtons[pos].setText(mContext.getResources().getString(
379                             R.string.grant_dialog_button_allow_foreground));
380                 }
381             }
382             if ((pos == DENY_BUTTON || pos == DENY_AND_DONT_ASK_AGAIN_BUTTON)) {
383                 if (
384                         mLocationVisibilities[LOCATION_ACCURACY_LAYOUT]
385                                 && mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]
386                 ) {
387                     mButtons[pos].setText(mContext.getResources().getString(
388                             R.string.grant_dialog_button_keey_approximate_location));
389                 } else {
390                     mButtons[pos].setText(
391                             mContext.getResources().getString(R.string.grant_dialog_button_deny));
392                 }
393             }
394             mButtons[pos].requestLayout();
395         }
396     }
397 
updateDetailDescription()398     private void updateDetailDescription() {
399         if (mDetailMessage == null) {
400             mDetailedMessageView.setVisibility(View.GONE);
401         } else {
402             mDetailedMessageView.setText(mDetailMessage);
403             mDetailedMessageView.setVisibility(View.VISIBLE);
404         }
405     }
406 
updateDescription()407     private void updateDescription() {
408         mMessageView.setText(mGroupMessage);
409     }
410 
setButtonVisibilities(boolean[] visibilities)411     private void setButtonVisibilities(boolean[] visibilities) {
412         // If GrantPermissionsActivity sent the user directly to settings, button visibilities are
413         // not created. If the activity was then destroyed by the system, once the activity is
414         // recreated to perform onActivityResult, it will try to loadInstanceState in onCreate but
415         // the button visibilities were never set, so they will be null.
416         mButtonVisibilities = visibilities == null ? new boolean[0] : visibilities;
417     }
418 
setLocationVisibilities(boolean[] visibilities)419     private void setLocationVisibilities(boolean[] visibilities) {
420         mLocationVisibilities = visibilities == null ? new boolean[0] : visibilities;
421     }
422 
423     @Override
onClick(View view)424     public void onClick(View view) {
425         int id = view.getId();
426 
427         if (id == R.id.permission_location_accuracy_radio_fine) {
428             if (mSelectedPrecision != FINE_RADIO_BUTTON) {
429                 ((RadioButton) mLocationViews[FINE_RADIO_BUTTON]).setChecked(true);
430                 mSelectedPrecision = FINE_RADIO_BUTTON;
431                 runLocationAccuracyAnimation(true);
432             }
433             return;
434         }
435 
436         if (id == R.id.permission_location_accuracy_radio_coarse) {
437             if (mSelectedPrecision != COARSE_RADIO_BUTTON) {
438                 ((RadioButton) mLocationViews[COARSE_RADIO_BUTTON]).setChecked(true);
439                 mSelectedPrecision = COARSE_RADIO_BUTTON;
440                 runLocationAccuracyAnimation(false);
441             }
442             return;
443         }
444 
445         if (mLocationVisibilities[LOCATION_ACCURACY_LAYOUT]) {
446             mIsLocationPermissionDialogActionClicked = true;
447         }
448 
449         if (id == R.id.grant_singleton) {
450             mResultListener.onPermissionGrantResult(mGroupName, CANCELED);
451             return;
452         }
453 
454         List<String> affectedForegroundPermissions = new ArrayList<>();
455         if (mLocationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) {
456             if (((RadioGroup) mLocationViews[DIALOG_WITH_BOTH_LOCATIONS])
457                     .getCheckedRadioButtonId() == R.id.permission_location_accuracy_radio_coarse) {
458 
459                 affectedForegroundPermissions.add(ACCESS_COARSE_LOCATION);
460             } else if (((RadioGroup) mLocationViews[DIALOG_WITH_BOTH_LOCATIONS])
461                     .getCheckedRadioButtonId() == R.id.permission_location_accuracy_radio_fine) {
462 
463                 affectedForegroundPermissions.add(ACCESS_FINE_LOCATION);
464                 affectedForegroundPermissions.add(ACCESS_COARSE_LOCATION);
465             }
466         } else if (mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) {
467             affectedForegroundPermissions.add(ACCESS_FINE_LOCATION);
468         } else if (mLocationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) {
469             affectedForegroundPermissions.add(ACCESS_COARSE_LOCATION);
470         }
471 
472 
473         Map<Integer, Integer> buttonToResultValueMap = initializeButtonToResultValueMap();
474         int resultValue = buttonToResultValueMap.getOrDefault(
475                 mButtonResIdToNum.get(id, -1), -1);
476         if (resultValue != -1) {
477             if (resultValue != DENIED_MORE) {
478                 view.performAccessibilityAction(
479                         AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
480             }
481             mResultListener.onPermissionGrantResult(mGroupName, affectedForegroundPermissions,
482                     resultValue);
483         }
484     }
485 
initializeButtonToResultValueMap()486     private Map<Integer, Integer> initializeButtonToResultValueMap() {
487         Map<Integer, Integer> map = new HashMap<>();
488         map.put(ALLOW_ALL_BUTTON, GRANTED_ALWAYS);
489         map.put(ALLOW_BUTTON, GRANTED_ALWAYS);
490         map.put(ALLOW_FOREGROUND_BUTTON, GRANTED_FOREGROUND_ONLY);
491         map.put(ALLOW_ALWAYS_BUTTON, GRANTED_ALWAYS);
492         map.put(ALLOW_ONE_TIME_BUTTON, GRANTED_ONE_TIME);
493         map.put(ALLOW_SELECTED_BUTTON, GRANTED_USER_SELECTED);
494         map.put(DONT_ALLOW_MORE_SELECTED_BUTTON, DENIED_MORE);
495         map.put(DENY_BUTTON, DENIED);
496         map.put(NO_UPGRADE_BUTTON, DENIED);
497         map.put(NO_UPGRADE_OT_BUTTON, DENIED);
498         map.put(DENY_AND_DONT_ASK_AGAIN_BUTTON, DENIED_DO_NOT_ASK_AGAIN);
499         map.put(NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON, DENIED_DO_NOT_ASK_AGAIN);
500         map.put(NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON, DENIED_DO_NOT_ASK_AGAIN);
501         return map;
502     }
503 
generateResIdToButtonMap()504     private void generateResIdToButtonMap() {
505         mButtonResIdToNum.put(R.id.permission_allow_button, ALLOW_BUTTON);
506         mButtonResIdToNum.put(
507                 R.id.permission_allow_foreground_only_button,
508                 ALLOW_FOREGROUND_BUTTON
509         );
510         mButtonResIdToNum.put(R.id.permission_deny_button, DENY_BUTTON);
511         mButtonResIdToNum.put(
512                 R.id.permission_deny_and_dont_ask_again_button,
513                 DENY_AND_DONT_ASK_AGAIN_BUTTON
514         );
515         mButtonResIdToNum.put(R.id.permission_allow_one_time_button, ALLOW_ONE_TIME_BUTTON);
516         mButtonResIdToNum.put(R.id.permission_no_upgrade_button, NO_UPGRADE_BUTTON);
517         mButtonResIdToNum.put(
518                 R.id.permission_no_upgrade_and_dont_ask_again_button,
519                 NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON
520         );
521         mButtonResIdToNum.put(
522                 R.id.permission_no_upgrade_one_time_button,
523                 NO_UPGRADE_OT_BUTTON
524         );
525         mButtonResIdToNum.put(
526                 R.id.permission_no_upgrade_one_time_and_dont_ask_again_button,
527                 NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON
528         );
529         mButtonResIdToNum.put(R.id.permission_allow_all_button, ALLOW_ALL_BUTTON);
530         mButtonResIdToNum.put(R.id.permission_allow_selected_button, ALLOW_SELECTED_BUTTON);
531         mButtonResIdToNum.put(
532                 R.id.permission_dont_allow_more_selected_button,
533                 DONT_ALLOW_MORE_SELECTED_BUTTON
534         );
535 
536         mLocationResIdToNum.put(R.id.permission_location_accuracy, LOCATION_ACCURACY_LAYOUT);
537         mLocationResIdToNum.put(
538                 R.id.permission_location_accuracy_radio_fine,
539                 FINE_RADIO_BUTTON
540         );
541         mLocationResIdToNum.put(
542                 R.id.permission_location_accuracy_radio_coarse,
543                 COARSE_RADIO_BUTTON
544         );
545         mLocationResIdToNum.put(
546                 R.id.permission_location_accuracy_radio_group,
547                 DIALOG_WITH_BOTH_LOCATIONS
548         );
549         mLocationResIdToNum.put(
550                 R.id.permission_location_accuracy_fine_only,
551                 DIALOG_WITH_FINE_LOCATION_ONLY
552         );
553         mLocationResIdToNum.put(
554                 R.id.permission_location_accuracy_coarse_only,
555                 DIALOG_WITH_COARSE_LOCATION_ONLY
556         );
557     }
558 }
559