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 
17 package com.android.settings.accessibility;
18 
19 import static com.android.settings.accessibility.AccessibilityStatsLogUtils.logAccessibilityServiceEnabled;
20 
21 import android.accessibilityservice.AccessibilityShortcutInfo;
22 import android.app.ActivityOptions;
23 import android.content.ActivityNotFoundException;
24 import android.content.ComponentName;
25 import android.content.ContentResolver;
26 import android.content.Intent;
27 import android.content.pm.ActivityInfo;
28 import android.net.Uri;
29 import android.os.Bundle;
30 import android.os.UserHandle;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.view.Menu;
34 import android.view.MenuInflater;
35 import android.view.View;
36 import android.view.accessibility.AccessibilityManager;
37 
38 import androidx.annotation.Nullable;
39 import androidx.preference.SwitchPreference;
40 
41 import com.android.settings.R;
42 
43 import java.util.List;
44 
45 /** Fragment for providing open activity button. */
46 public class LaunchAccessibilityActivityPreferenceFragment extends
47         ToggleFeaturePreferenceFragment {
48     private static final String TAG = "LaunchA11yActivity";
49     private static final String EMPTY_STRING = "";
50 
51     @Override
onViewCreated(View view, Bundle savedInstanceState)52     public void onViewCreated(View view, Bundle savedInstanceState) {
53         super.onViewCreated(view, savedInstanceState);
54 
55         mToggleServiceDividerSwitchPreference.setSwitchVisibility(View.GONE);
56     }
57 
58     @Override
onPreferenceToggled(String preferenceKey, boolean enabled)59     protected void onPreferenceToggled(String preferenceKey, boolean enabled) {
60         logAccessibilityServiceEnabled(mComponentName, enabled);
61         launchShortcutTargetActivity(getPrefContext().getDisplayId(), mComponentName);
62     }
63 
64     @Override
onInstallSwitchPreferenceToggleSwitch()65     protected void onInstallSwitchPreferenceToggleSwitch() {
66         super.onInstallSwitchPreferenceToggleSwitch();
67         mToggleServiceDividerSwitchPreference.setOnPreferenceClickListener((preference) -> {
68             final boolean checked = ((DividerSwitchPreference) preference).isChecked();
69             onPreferenceToggled(mPreferenceKey, checked);
70             return false;
71         });
72     }
73 
74     @Override
onProcessArguments(Bundle arguments)75     protected void onProcessArguments(Bundle arguments) {
76         super.onProcessArguments(arguments);
77 
78         mComponentName = arguments.getParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME);
79         final ActivityInfo info = getAccessibilityShortcutInfo().getActivityInfo();
80         mPackageName = info.loadLabel(getPackageManager()).toString();
81 
82         // Settings animated image.
83         final int animatedImageRes = arguments.getInt(
84                 AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES);
85         mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
86                 .authority(mComponentName.getPackageName())
87                 .appendPath(String.valueOf(animatedImageRes))
88                 .build();
89 
90         // Settings html description.
91         mHtmlDescription = arguments.getCharSequence(AccessibilitySettings.EXTRA_HTML_DESCRIPTION);
92 
93         // Settings title and intent.
94         final String settingsTitle = arguments.getString(
95                 AccessibilitySettings.EXTRA_SETTINGS_TITLE);
96         mSettingsIntent = TextUtils.isEmpty(settingsTitle) ? null : getSettingsIntent(arguments);
97         mSettingsTitle = (mSettingsIntent == null) ? null : settingsTitle;
98     }
99 
100     @Override
onSettingsClicked(ShortcutPreference preference)101     public void onSettingsClicked(ShortcutPreference preference) {
102         super.onSettingsClicked(preference);
103         showDialog(DialogEnums.EDIT_SHORTCUT);
104     }
105 
106     @Override
getUserShortcutTypes()107     int getUserShortcutTypes() {
108         return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(),
109                 mComponentName);
110     }
111 
112     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)113     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
114         // Do not call super. We don't want to see the "Help & feedback" option on this page so as
115         // not to confuse users who think they might be able to send feedback about a specific
116         // accessibility service from this page.
117     }
118 
119     @Override
updateToggleServiceTitle(SwitchPreference switchPreference)120     protected void updateToggleServiceTitle(SwitchPreference switchPreference) {
121         final AccessibilityShortcutInfo info = getAccessibilityShortcutInfo();
122         final String switchBarText = (info == null) ? EMPTY_STRING : getString(
123                 R.string.accessibility_service_master_open_title,
124                 info.getActivityInfo().loadLabel(getPackageManager()));
125 
126         switchPreference.setTitle(switchBarText);
127     }
128 
129     // IMPORTANT: Refresh the info since there are dynamically changing capabilities.
getAccessibilityShortcutInfo()130     private AccessibilityShortcutInfo getAccessibilityShortcutInfo() {
131         final List<AccessibilityShortcutInfo> infos = AccessibilityManager.getInstance(
132                 getPrefContext()).getInstalledAccessibilityShortcutListAsUser(getPrefContext(),
133                 UserHandle.myUserId());
134 
135         for (int i = 0, count = infos.size(); i < count; i++) {
136             AccessibilityShortcutInfo shortcutInfo = infos.get(i);
137             ActivityInfo activityInfo = shortcutInfo.getActivityInfo();
138             if (mComponentName.getPackageName().equals(activityInfo.packageName)
139                     && mComponentName.getClassName().equals(activityInfo.name)) {
140                 return shortcutInfo;
141             }
142         }
143         return null;
144     }
145 
launchShortcutTargetActivity(int displayId, ComponentName name)146     private void launchShortcutTargetActivity(int displayId, ComponentName name) {
147         final Intent intent = new Intent();
148         final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle();
149 
150         intent.setComponent(name);
151         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
152         try {
153             final int userId = UserHandle.myUserId();
154             getPrefContext().startActivityAsUser(intent, bundle, UserHandle.of(userId));
155         } catch (ActivityNotFoundException ignore) {
156             // ignore the exception
157             Log.w(TAG, "Target activity not found.");
158         }
159     }
160 
161     @Nullable
getSettingsIntent(Bundle arguments)162     private Intent getSettingsIntent(Bundle arguments) {
163         final String settingsComponentName = arguments.getString(
164                 AccessibilitySettings.EXTRA_SETTINGS_COMPONENT_NAME);
165         if (TextUtils.isEmpty(settingsComponentName)) {
166             return null;
167         }
168 
169         final Intent settingsIntent = new Intent(Intent.ACTION_MAIN).setComponent(
170                 ComponentName.unflattenFromString(settingsComponentName));
171         if (getPackageManager().queryIntentActivities(settingsIntent, /* flags= */ 0).isEmpty()) {
172             return null;
173         }
174 
175         return settingsIntent;
176     }
177 }
178