• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * 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.wear;
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_ALWAYS_BUTTON;
23 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_BUTTON;
24 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_FOREGROUND_BUTTON;
25 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.ALLOW_ONE_TIME_BUTTON;
26 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_AND_DONT_ASK_AGAIN_BUTTON;
27 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DENY_BUTTON;
28 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_BOTH_LOCATIONS;
29 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_COARSE_LOCATION_ONLY;
30 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.DIALOG_WITH_FINE_LOCATION_ONLY;
31 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.FINE_RADIO_BUTTON;
32 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_BUTTON;
33 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NEXT_LOCATION_DIALOG;
34 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON;
35 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_BUTTON;
36 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON;
37 import static com.android.permissioncontroller.permission.ui.GrantPermissionsActivity.NO_UPGRADE_OT_BUTTON;
38 
39 import android.app.Activity;
40 import android.graphics.drawable.Icon;
41 import android.os.Bundle;
42 import android.util.SparseIntArray;
43 import android.view.View;
44 import android.view.WindowManager.LayoutParams;
45 
46 import androidx.annotation.Nullable;
47 import androidx.compose.ui.platform.ComposeView;
48 
49 import com.android.permissioncontroller.R;
50 import com.android.permissioncontroller.permission.ui.GrantPermissionsViewHandler;
51 import com.android.permissioncontroller.permission.ui.wear.model.WearGrantPermissionsViewModel;
52 import com.android.permissioncontroller.permission.ui.wear.model.WearGrantPermissionsViewModelFactory;
53 
54 import kotlin.Unit;
55 
56 import java.util.Arrays;
57 import java.util.Collections;
58 import java.util.List;
59 
60 /**
61  * Wear-specific view handler for the grant permissions activity.
62  */
63 public class GrantPermissionsWearViewHandler implements GrantPermissionsViewHandler {
64 
65     public static final String ARG_GROUP_NAME = "ARG_GROUP_NAME";
66     public static final String ARG_GROUP_COUNT = "ARG_GROUP_COUNT";
67     public static final String ARG_GROUP_INDEX = "ARG_GROUP_INDEX";
68     public static final String ARG_GROUP_ICON = "ARG_GROUP_ICON";
69     public static final String ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE";
70     private static final String ARG_GROUP_DETAIL_MESSAGE = "ARG_GROUP_DETAIL_MESSAGE";
71     private static final String ARG_DIALOG_BUTTON_VISIBILITIES = "ARG_DIALOG_BUTTON_VISIBILITIES";
72     private static final String ARG_DIALOG_LOCATION_VISIBILITIES =
73             "ARG_DIALOG_LOCATION_VISIBILITIES";
74 
75     public static final SparseIntArray BUTTON_RES_ID_TO_NUM = new SparseIntArray();
76     static {
BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_button, ALLOW_BUTTON)77         BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_button, ALLOW_BUTTON);
BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_always_button, ALLOW_ALWAYS_BUTTON)78         BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_always_button,
79                 ALLOW_ALWAYS_BUTTON);
BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_foreground_only_button, ALLOW_FOREGROUND_BUTTON)80         BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_foreground_only_button,
81                 ALLOW_FOREGROUND_BUTTON);
BUTTON_RES_ID_TO_NUM.put(R.id.permission_deny_button, DENY_BUTTON)82         BUTTON_RES_ID_TO_NUM.put(R.id.permission_deny_button, DENY_BUTTON);
BUTTON_RES_ID_TO_NUM.put(R.id.permission_deny_and_dont_ask_again_button, DENY_AND_DONT_ASK_AGAIN_BUTTON)83         BUTTON_RES_ID_TO_NUM.put(R.id.permission_deny_and_dont_ask_again_button,
84                 DENY_AND_DONT_ASK_AGAIN_BUTTON);
BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_one_time_button, ALLOW_ONE_TIME_BUTTON)85         BUTTON_RES_ID_TO_NUM.put(R.id.permission_allow_one_time_button, ALLOW_ONE_TIME_BUTTON);
BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_button, NO_UPGRADE_BUTTON)86         BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_button, NO_UPGRADE_BUTTON);
BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_and_dont_ask_again_button, NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON)87         BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_and_dont_ask_again_button,
88                 NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON);
BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_button, NO_UPGRADE_OT_BUTTON)89         BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_button, NO_UPGRADE_OT_BUTTON);
BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_and_dont_ask_again_button, NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON)90         BUTTON_RES_ID_TO_NUM.put(R.id.permission_no_upgrade_one_time_and_dont_ask_again_button,
91                 NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON);
92     }
93 
94     private final Activity mActivity;
95 
96     private ResultListener mResultListener;
97 
98     // Configuration of the current dialog
99     private String mGroupName;
100     private int mGroupCount;
101     private int mGroupIndex;
102     private Icon mGroupIcon;
103     private CharSequence mGroupMessage;
104     private CharSequence mDetailMessage;
105     private final boolean[] mButtonVisibilities = new boolean[NEXT_BUTTON];
106     private final boolean[] mLocationVisibilities = new boolean[NEXT_LOCATION_DIALOG];
107     // View model to update WearGrantPermissionsScreen elements
108     private WearGrantPermissionsViewModel mViewModel;
109 
GrantPermissionsWearViewHandler(Activity activity)110     public GrantPermissionsWearViewHandler(Activity activity) {
111         mActivity = activity;
112     }
113 
114     @Override
setResultListener(ResultListener listener)115     public GrantPermissionsWearViewHandler setResultListener(ResultListener listener) {
116         mResultListener = listener;
117         return this;
118     }
119 
120     @Override
saveInstanceState(Bundle arguments)121     public void saveInstanceState(Bundle arguments) {
122         arguments.putString(ARG_GROUP_NAME, mGroupName);
123         arguments.putInt(ARG_GROUP_COUNT, mGroupCount);
124         arguments.putInt(ARG_GROUP_INDEX, mGroupIndex);
125         arguments.putParcelable(ARG_GROUP_ICON, mGroupIcon);
126         arguments.putCharSequence(ARG_GROUP_MESSAGE, mGroupMessage);
127         arguments.putCharSequence(ARG_GROUP_DETAIL_MESSAGE, mDetailMessage);
128         arguments.putBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES, mButtonVisibilities);
129         arguments.putBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES, mLocationVisibilities);
130     }
131 
132     @Override
loadInstanceState(Bundle savedInstanceState)133     public void loadInstanceState(Bundle savedInstanceState) {
134         mGroupName = savedInstanceState.getString(ARG_GROUP_NAME);
135         mGroupMessage = savedInstanceState.getCharSequence(ARG_GROUP_MESSAGE);
136         mGroupIcon = savedInstanceState.getParcelable(ARG_GROUP_ICON);
137         mGroupCount = savedInstanceState.getInt(ARG_GROUP_COUNT);
138         mGroupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX);
139         mDetailMessage = savedInstanceState.getCharSequence(ARG_GROUP_DETAIL_MESSAGE);
140         setButtonVisibilities(savedInstanceState.getBooleanArray(ARG_DIALOG_BUTTON_VISIBILITIES));
141         setLocationVisibilities(
142                 savedInstanceState.getBooleanArray(ARG_DIALOG_LOCATION_VISIBILITIES));
143 
144         updateScreen();
145     }
146 
147     @Override
updateUi(String groupName, int groupCount, int groupIndex, Icon icon, CharSequence message, CharSequence detailMessage, CharSequence permissionRationaleMessage, boolean[] buttonVisibilities, boolean[] locationVisibilities)148     public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
149             CharSequence message, CharSequence detailMessage,
150             CharSequence permissionRationaleMessage, boolean[] buttonVisibilities,
151             boolean[] locationVisibilities) {
152         // permissionRationaleMessage ignored by wear
153         if (mActivity.isFinishing()) {
154             return;
155         }
156         mGroupName = groupName;
157         mGroupCount = groupCount;
158         mGroupIndex = groupIndex;
159         mGroupIcon = icon;
160         mGroupMessage = message;
161         mDetailMessage = detailMessage;
162         setButtonVisibilities(buttonVisibilities);
163         setLocationVisibilities(locationVisibilities);
164 
165         updateScreen();
166     }
167 
updateScreen()168     private void updateScreen() {
169         mViewModel.getIconLiveData().setValue(
170                 mGroupIcon == null ? null : mGroupIcon.loadDrawable(mActivity));
171         mViewModel.getGroupMessageLiveData().setValue(
172                 mGroupMessage == null ? "" : mGroupMessage.toString());
173         mViewModel.getDetailMessageLiveData().setValue(mDetailMessage);
174         int numButtons = BUTTON_RES_ID_TO_NUM.size();
175         List<Boolean> buttonVisibilityList = Arrays.asList(new Boolean[NEXT_BUTTON]);
176         for (int i = 0; i < numButtons; i++) {
177             int pos = BUTTON_RES_ID_TO_NUM.valueAt(i);
178             buttonVisibilityList.set(pos, mButtonVisibilities[pos] ? Boolean.TRUE : Boolean.FALSE);
179         }
180         mViewModel.getButtonVisibilitiesLiveData().setValue(buttonVisibilityList);
181         List<Boolean> locationVisibilityList = Arrays.asList(new Boolean[NEXT_LOCATION_DIALOG]);
182         for (int i = 0; i < locationVisibilityList.size(); i++) {
183             locationVisibilityList.set(i, mLocationVisibilities[i] ? Boolean.TRUE : Boolean.FALSE);
184         }
185         mViewModel.getLocationVisibilitiesLiveData().setValue(locationVisibilityList);
186         if (mLocationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) {
187             mViewModel.getPreciseLocationCheckedLiveData()
188                     .setValue(mLocationVisibilities[FINE_RADIO_BUTTON]);
189         } else if (mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]
190                 || mLocationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) {
191             mViewModel.getPreciseLocationCheckedLiveData().setValue(false);
192         }
193         mViewModel.getShowDialog().postValue(true);
194     }
195 
onLocationSwitchChanged(boolean checked)196     private void onLocationSwitchChanged(boolean checked) {
197         mViewModel.getPreciseLocationCheckedLiveData().setValue(checked);
198     }
199 
onButtonClicked(int id)200     private void onButtonClicked(int id) {
201         List<String> affectedForegroundPermissions = null;
202         if (mLocationVisibilities[DIALOG_WITH_BOTH_LOCATIONS]) {
203             if (Boolean.TRUE.equals(mViewModel.getPreciseLocationCheckedLiveData().getValue())) {
204                 affectedForegroundPermissions = Arrays.asList(
205                         ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION);
206             } else {
207                 affectedForegroundPermissions = Collections.singletonList(ACCESS_COARSE_LOCATION);
208             }
209         } else if (mLocationVisibilities[DIALOG_WITH_FINE_LOCATION_ONLY]) {
210             affectedForegroundPermissions = Collections.singletonList(ACCESS_FINE_LOCATION);
211         } else if (mLocationVisibilities[DIALOG_WITH_COARSE_LOCATION_ONLY]) {
212             affectedForegroundPermissions = Collections.singletonList(ACCESS_COARSE_LOCATION);
213         }
214 
215         int button = -1;
216         try {
217             button = BUTTON_RES_ID_TO_NUM.get(id);
218         } catch (NullPointerException e) {
219             // Clicked a view which is not one of the defined buttons
220             return;
221         }
222         // TODO (b/297534305): Research on performAccessibilityAction().
223         switch (button) {
224             case ALLOW_BUTTON:
225                 if (mResultListener != null) {
226                     mResultListener.onPermissionGrantResult(mGroupName,
227                             affectedForegroundPermissions, GRANTED_ALWAYS);
228                 }
229                 break;
230             case ALLOW_FOREGROUND_BUTTON:
231                 if (mResultListener != null) {
232                     mResultListener.onPermissionGrantResult(mGroupName,
233                             affectedForegroundPermissions, GRANTED_FOREGROUND_ONLY);
234                 }
235                 break;
236             case ALLOW_ALWAYS_BUTTON:
237                 if (mResultListener != null) {
238                     mResultListener.onPermissionGrantResult(mGroupName,
239                             affectedForegroundPermissions, GRANTED_ALWAYS);
240                 }
241                 break;
242             case ALLOW_ONE_TIME_BUTTON:
243                 if (mResultListener != null) {
244                     mResultListener.onPermissionGrantResult(mGroupName,
245                             affectedForegroundPermissions, GRANTED_ONE_TIME);
246                 }
247                 break;
248             case DENY_BUTTON:
249             case NO_UPGRADE_BUTTON:
250             case NO_UPGRADE_OT_BUTTON:
251                 if (mResultListener != null) {
252                     mResultListener.onPermissionGrantResult(mGroupName,
253                             affectedForegroundPermissions, DENIED);
254                 }
255                 break;
256             case DENY_AND_DONT_ASK_AGAIN_BUTTON:
257             case NO_UPGRADE_AND_DONT_ASK_AGAIN_BUTTON:
258             case NO_UPGRADE_OT_AND_DONT_ASK_AGAIN_BUTTON:
259                 if (mResultListener != null) {
260                     mResultListener.onPermissionGrantResult(mGroupName,
261                             affectedForegroundPermissions, DENIED_DO_NOT_ASK_AGAIN);
262                 }
263                 break;
264         }
265     }
266 
267     @Override
createView()268     public View createView() {
269         WearGrantPermissionsViewModelFactory factory = new WearGrantPermissionsViewModelFactory();
270         mViewModel = factory.create(WearGrantPermissionsViewModel.class);
271         ComposeView root = new ComposeView(mActivity);
272 
273         WearGrantPermissionsScreenKt.setContent(root,
274                 mViewModel,
275                 () -> {
276                     if (mResultListener != null) {
277                         mResultListener.onPermissionGrantResult(null, null, CANCELED);
278                     }
279                     return Unit.INSTANCE;
280                 },
281                 id -> {
282                     onButtonClicked(id);
283                     return Unit.INSTANCE;
284                 },
285                 checked -> {
286                     onLocationSwitchChanged(checked);
287                     return Unit.INSTANCE;
288                 }
289         );
290         if (mGroupName != null) {
291             updateScreen();
292         }
293 
294         return root;
295     }
296 
297     @Override
updateWindowAttributes(LayoutParams outLayoutParams)298     public void updateWindowAttributes(LayoutParams outLayoutParams) {
299         // No-op
300     }
301 
setButtonVisibilities(@ullable boolean[] visibilities)302     private void setButtonVisibilities(@Nullable boolean[] visibilities) {
303         for (int i = 0; i < mButtonVisibilities.length; i++) {
304             mButtonVisibilities[i] =
305                     visibilities != null && i < visibilities.length && visibilities[i];
306         }
307     }
308 
309     private void setLocationVisibilities(@Nullable boolean[] visibilities) {
310         for (int i = 0; i < mLocationVisibilities.length; i++) {
311             mLocationVisibilities[i] =
312                     visibilities != null && i < visibilities.length && visibilities[i];
313         }
314     }
315 
316     @Override
317     public void onBackPressed() {
318         if (mResultListener != null) {
319             mResultListener.onPermissionGrantResult(mGroupName, CANCELED);
320         } else {
321             mActivity.finish();
322         }
323     }
324 }
325