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