1 /*
2  * Copyright (C) 2020 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 package com.android.internal.accessibility.dialog;
17 
18 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
19 import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
20 import static android.view.accessibility.AccessibilityManager.ShortcutType;
21 
22 import static com.android.internal.accessibility.common.ShortcutConstants.ShortcutMenuMode;
23 import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.createEnableDialogContentView;
24 import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getInstalledTargets;
25 import static com.android.internal.accessibility.dialog.AccessibilityTargetHelper.getTargets;
26 import static com.android.internal.accessibility.util.AccessibilityUtils.isUserSetupCompleted;
27 
28 import android.annotation.Nullable;
29 import android.app.Activity;
30 import android.app.AlertDialog;
31 import android.content.DialogInterface;
32 import android.content.res.TypedArray;
33 import android.os.Bundle;
34 import android.view.View;
35 import android.view.Window;
36 import android.widget.AdapterView;
37 
38 import com.android.internal.R;
39 
40 import java.util.ArrayList;
41 import java.util.List;
42 
43 /**
44  * Activity used to display various targets related to accessibility service, accessibility
45  * activity or allowlisting feature for volume key shortcut.
46  */
47 public class AccessibilityShortcutChooserActivity extends Activity {
48     @ShortcutType
49     private final int mShortcutType = ACCESSIBILITY_SHORTCUT_KEY;
50     private static final String KEY_ACCESSIBILITY_SHORTCUT_MENU_MODE =
51             "accessibility_shortcut_menu_mode";
52     private final List<AccessibilityTarget> mTargets = new ArrayList<>();
53     private AlertDialog mMenuDialog;
54     private AlertDialog mPermissionDialog;
55     private ShortcutTargetAdapter mTargetAdapter;
56 
57     @Override
onCreate(@ullable Bundle savedInstanceState)58     protected void onCreate(@Nullable Bundle savedInstanceState) {
59         super.onCreate(savedInstanceState);
60 
61         final TypedArray theme = getTheme().obtainStyledAttributes(android.R.styleable.Theme);
62         if (!theme.getBoolean(android.R.styleable.Theme_windowNoTitle, /* defValue= */ false)) {
63             requestWindowFeature(Window.FEATURE_NO_TITLE);
64         }
65 
66         mTargets.addAll(getTargets(this, mShortcutType));
67         mTargetAdapter = new ShortcutTargetAdapter(mTargets);
68         mMenuDialog = createMenuDialog();
69         mMenuDialog.setOnShowListener(dialog -> updateDialogListeners());
70         mMenuDialog.show();
71 
72         if (savedInstanceState != null) {
73             final int restoreShortcutMenuMode =
74                     savedInstanceState.getInt(KEY_ACCESSIBILITY_SHORTCUT_MENU_MODE,
75                             ShortcutMenuMode.LAUNCH);
76             if (restoreShortcutMenuMode == ShortcutMenuMode.EDIT) {
77                 onEditButtonClicked();
78             }
79         }
80     }
81 
82     @Override
onDestroy()83     protected void onDestroy() {
84         mMenuDialog.setOnDismissListener(null);
85         mMenuDialog.dismiss();
86         super.onDestroy();
87     }
88 
89     @Override
onSaveInstanceState(Bundle outState)90     protected void onSaveInstanceState(Bundle outState) {
91         super.onSaveInstanceState(outState);
92         outState.putInt(KEY_ACCESSIBILITY_SHORTCUT_MENU_MODE, mTargetAdapter.getShortcutMenuMode());
93     }
94 
onTargetSelected(AdapterView<?> parent, View view, int position, long id)95     private void onTargetSelected(AdapterView<?> parent, View view, int position, long id) {
96         final AccessibilityTarget target = mTargets.get(position);
97         target.onSelected();
98         mMenuDialog.dismiss();
99     }
100 
onTargetChecked(AdapterView<?> parent, View view, int position, long id)101     private void onTargetChecked(AdapterView<?> parent, View view, int position, long id) {
102         final AccessibilityTarget target = mTargets.get(position);
103 
104         if ((target instanceof AccessibilityServiceTarget) && !target.isShortcutEnabled()) {
105             mPermissionDialog = new AlertDialog.Builder(this)
106                     .setView(createEnableDialogContentView(this,
107                             (AccessibilityServiceTarget) target,
108                             v -> {
109                                 mPermissionDialog.dismiss();
110                                 mTargetAdapter.notifyDataSetChanged();
111                             },
112                             v -> mPermissionDialog.dismiss()))
113                     .create();
114             mPermissionDialog.show();
115             return;
116         }
117 
118         target.onCheckedChanged(!target.isShortcutEnabled());
119         mTargetAdapter.notifyDataSetChanged();
120     }
121 
onDoneButtonClicked()122     private void onDoneButtonClicked() {
123         mTargets.clear();
124         mTargets.addAll(getTargets(this, mShortcutType));
125         if (mTargets.isEmpty()) {
126             mMenuDialog.dismiss();
127             return;
128         }
129 
130         mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.LAUNCH);
131         mTargetAdapter.notifyDataSetChanged();
132 
133         mMenuDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
134                 getString(R.string.edit_accessibility_shortcut_menu_button));
135 
136         updateDialogListeners();
137     }
138 
onEditButtonClicked()139     private void onEditButtonClicked() {
140         mTargets.clear();
141         mTargets.addAll(getInstalledTargets(this, mShortcutType));
142         mTargetAdapter.setShortcutMenuMode(ShortcutMenuMode.EDIT);
143         mTargetAdapter.notifyDataSetChanged();
144 
145         mMenuDialog.getButton(DialogInterface.BUTTON_POSITIVE).setText(
146                 getString(R.string.done_accessibility_shortcut_menu_button));
147 
148         updateDialogListeners();
149     }
150 
updateDialogListeners()151     private void updateDialogListeners() {
152         final boolean isEditMenuMode =
153                 mTargetAdapter.getShortcutMenuMode() == ShortcutMenuMode.EDIT;
154         final int selectDialogTitleId = R.string.accessibility_select_shortcut_menu_title;
155         final int editDialogTitleId =
156                 mShortcutType == ACCESSIBILITY_BUTTON
157                         ? R.string.accessibility_edit_shortcut_menu_button_title
158                         : R.string.accessibility_edit_shortcut_menu_volume_title;
159 
160         mMenuDialog.setTitle(getString(isEditMenuMode ? editDialogTitleId : selectDialogTitleId));
161         mMenuDialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(
162                 isEditMenuMode ? view -> onDoneButtonClicked() : view -> onEditButtonClicked());
163         mMenuDialog.getListView().setOnItemClickListener(
164                 isEditMenuMode ? this::onTargetChecked : this::onTargetSelected);
165     }
166 
createMenuDialog()167     private AlertDialog createMenuDialog() {
168         final String dialogTitle =
169                 getString(R.string.accessibility_select_shortcut_menu_title);
170 
171         final AlertDialog.Builder builder = new AlertDialog.Builder(this)
172                 .setTitle(dialogTitle)
173                 .setAdapter(mTargetAdapter, /* listener= */ null)
174                 .setOnDismissListener(dialog -> finish());
175 
176         if (isUserSetupCompleted(this)) {
177             final String positiveButtonText =
178                     getString(R.string.edit_accessibility_shortcut_menu_button);
179             builder.setPositiveButton(positiveButtonText, /* listener= */ null);
180         }
181 
182         return builder.create();
183     }
184 }
185