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 import com.android.settings.accessibility.AccessibilityUtil.QuickSettingsTooltipType; 45 46 import java.util.ArrayList; 47 import java.util.List; 48 49 /** Fragment for providing open activity button. */ 50 public class LaunchAccessibilityActivityPreferenceFragment extends ToggleFeaturePreferenceFragment { 51 52 private static final String TAG = "LaunchAccessibilityActivityPreferenceFragment"; 53 private static final String EMPTY_STRING = ""; 54 protected static final String KEY_LAUNCH_PREFERENCE = "launch_preference"; 55 56 private ComponentName mTileComponentName; 57 58 @Override getMetricsCategory()59 public int getMetricsCategory() { 60 return getArguments().getInt(AccessibilitySettings.EXTRA_METRICS_CATEGORY); 61 } 62 63 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)64 public View onCreateView(LayoutInflater inflater, ViewGroup container, 65 Bundle savedInstanceState) { 66 // Init new preference to replace the switch preference instead. 67 initLaunchPreference(); 68 69 final View view = super.onCreateView(inflater, container, savedInstanceState); 70 removePreference(KEY_USE_SERVICE_PREFERENCE); 71 return view; 72 } 73 74 @Override onPreferenceToggled(String preferenceKey, boolean enabled)75 protected void onPreferenceToggled(String preferenceKey, boolean enabled) { 76 // Do nothing. 77 } 78 79 @Override onProcessArguments(Bundle arguments)80 protected void onProcessArguments(Bundle arguments) { 81 super.onProcessArguments(arguments); 82 mComponentName = arguments.getParcelable(AccessibilitySettings.EXTRA_COMPONENT_NAME); 83 final ActivityInfo info = getAccessibilityShortcutInfo().getActivityInfo(); 84 mPackageName = info.loadLabel(getPackageManager()).toString(); 85 86 // Settings animated image. 87 final int animatedImageRes = arguments.getInt( 88 AccessibilitySettings.EXTRA_ANIMATED_IMAGE_RES); 89 if (animatedImageRes > 0) { 90 mImageUri = new Uri.Builder().scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) 91 .authority(mComponentName.getPackageName()) 92 .appendPath(String.valueOf(animatedImageRes)) 93 .build(); 94 } 95 96 // Settings html description. 97 mHtmlDescription = arguments.getCharSequence(AccessibilitySettings.EXTRA_HTML_DESCRIPTION); 98 99 // Settings title and intent. 100 final String settingsTitle = arguments.getString( 101 AccessibilitySettings.EXTRA_SETTINGS_TITLE); 102 mSettingsIntent = TextUtils.isEmpty(settingsTitle) ? null : getSettingsIntent(arguments); 103 mSettingsTitle = (mSettingsIntent == null) ? null : settingsTitle; 104 105 // Tile service. 106 if (arguments.containsKey(AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME)) { 107 final String tileServiceComponentName = arguments.getString( 108 AccessibilitySettings.EXTRA_TILE_SERVICE_COMPONENT_NAME); 109 mTileComponentName = ComponentName.unflattenFromString(tileServiceComponentName); 110 } 111 } 112 113 @Override getUserShortcutTypes()114 int getUserShortcutTypes() { 115 return AccessibilityUtil.getUserShortcutTypesFromSettings(getPrefContext(), 116 mComponentName); 117 } 118 119 @Override getTileComponentName()120 ComponentName getTileComponentName() { 121 return mTileComponentName; 122 } 123 124 @Override getTileTooltipContent(@uickSettingsTooltipType int type)125 CharSequence getTileTooltipContent(@QuickSettingsTooltipType int type) { 126 final ComponentName componentName = getTileComponentName(); 127 if (componentName == null) { 128 return null; 129 } 130 131 final CharSequence tileName = loadTileLabel(getPrefContext(), componentName); 132 if (tileName == null) { 133 return null; 134 } 135 136 final int titleResId = type == QuickSettingsTooltipType.GUIDE_TO_EDIT 137 ? R.string.accessibility_service_qs_tooltip_content 138 : R.string.accessibility_service_auto_added_qs_tooltip_content; 139 return getString(titleResId, tileName); 140 } 141 142 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)143 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 144 // Do not call super. We don't want to see the "Help & feedback" option on this page so as 145 // not to confuse users who think they might be able to send feedback about a specific 146 // accessibility service from this page. 147 } 148 149 // IMPORTANT: Refresh the info since there are dynamically changing capabilities. getAccessibilityShortcutInfo()150 private AccessibilityShortcutInfo getAccessibilityShortcutInfo() { 151 final List<AccessibilityShortcutInfo> infos = AccessibilityManager.getInstance( 152 getPrefContext()).getInstalledAccessibilityShortcutListAsUser(getPrefContext(), 153 UserHandle.myUserId()); 154 155 for (int i = 0, count = infos.size(); i < count; i++) { 156 AccessibilityShortcutInfo shortcutInfo = infos.get(i); 157 ActivityInfo activityInfo = shortcutInfo.getActivityInfo(); 158 if (mComponentName.getPackageName().equals(activityInfo.packageName) 159 && mComponentName.getClassName().equals(activityInfo.name)) { 160 return shortcutInfo; 161 } 162 } 163 return null; 164 } 165 166 /** Customizes the order by preference key. */ getPreferenceOrderList()167 protected List<String> getPreferenceOrderList() { 168 final List<String> lists = new ArrayList<>(); 169 lists.add(KEY_TOP_INTRO_PREFERENCE); 170 lists.add(KEY_ANIMATED_IMAGE); 171 lists.add(KEY_LAUNCH_PREFERENCE); 172 lists.add(KEY_GENERAL_CATEGORY); 173 lists.add(KEY_HTML_DESCRIPTION_PREFERENCE); 174 return lists; 175 } 176 initLaunchPreference()177 private void initLaunchPreference() { 178 final Preference launchPreference = new Preference(getPrefContext()); 179 launchPreference.setLayoutResource(R.layout.accessibility_launch_activity_preference); 180 launchPreference.setKey(KEY_LAUNCH_PREFERENCE); 181 182 final AccessibilityShortcutInfo info = getAccessibilityShortcutInfo(); 183 final String switchBarText = (info == null) ? EMPTY_STRING : getString( 184 R.string.accessibility_service_primary_open_title, 185 info.getActivityInfo().loadLabel(getPackageManager())); 186 launchPreference.setTitle(switchBarText); 187 188 launchPreference.setOnPreferenceClickListener(preference -> { 189 logAccessibilityServiceEnabled(mComponentName, /* enabled= */ true); 190 launchShortcutTargetActivity(getPrefContext().getDisplayId(), mComponentName); 191 return true; 192 }); 193 getPreferenceScreen().addPreference(launchPreference); 194 } 195 launchShortcutTargetActivity(int displayId, ComponentName name)196 private void launchShortcutTargetActivity(int displayId, ComponentName name) { 197 final Intent intent = new Intent(); 198 final Bundle bundle = ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(); 199 200 intent.setComponent(name); 201 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 202 try { 203 final int userId = UserHandle.myUserId(); 204 getPrefContext().startActivityAsUser(intent, bundle, UserHandle.of(userId)); 205 } catch (ActivityNotFoundException ignore) { 206 // ignore the exception 207 Log.w(TAG, "Target activity not found."); 208 } 209 } 210 211 @Nullable getSettingsIntent(Bundle arguments)212 private Intent getSettingsIntent(Bundle arguments) { 213 final String settingsComponentName = arguments.getString( 214 AccessibilitySettings.EXTRA_SETTINGS_COMPONENT_NAME); 215 if (TextUtils.isEmpty(settingsComponentName)) { 216 return null; 217 } 218 219 final Intent settingsIntent = new Intent(Intent.ACTION_MAIN).setComponent( 220 ComponentName.unflattenFromString(settingsComponentName)); 221 if (getPackageManager().queryIntentActivities(settingsIntent, /* flags= */ 0).isEmpty()) { 222 return null; 223 } 224 225 return settingsIntent; 226 } 227 228 @Override getPreferenceScreenResId()229 protected int getPreferenceScreenResId() { 230 // TODO(b/171272809): Add back when controllers move to static type 231 return 0; 232 } 233 234 @Override getLogTag()235 protected String getLogTag() { 236 return TAG; 237 } 238 } 239