Target selection for Accessibility button
Allows for selecting a single accessibility service or feature
to be triggered by the accessibility button in the navigation bar
Bug: 34720082
Test: Manual
Change-Id: I320febede0398b1eff38a87d4db31fd7dfc53062
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 58595c2..de677f6 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5511,6 +5511,16 @@
"accessibility_shortcut_target_service";
/**
+ * Setting specifying the accessibility service or feature to be toggled via the
+ * accessibility button in the navigation bar. This is either a flattened
+ * {@link ComponentName} or the class name of a system class implementing a supported
+ * accessibility feature.
+ * @hide
+ */
+ public static final String ACCESSIBILITY_BUTTON_TARGET_COMPONENT =
+ "accessibility_button_target_component";
+
+ /**
* If touch exploration is enabled.
*/
public static final String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
@@ -6997,6 +7007,7 @@
TOUCH_EXPLORATION_ENABLED,
ACCESSIBILITY_ENABLED,
ACCESSIBILITY_SHORTCUT_TARGET_SERVICE,
+ ACCESSIBILITY_BUTTON_TARGET_COMPONENT,
ACCESSIBILITY_SHORTCUT_DIALOG_SHOWN,
ACCESSIBILITY_SHORTCUT_ENABLED,
ACCESSIBILITY_SHORTCUT_ON_LOCK_SCREEN,
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index c9f9f31..35276cc 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -22,6 +22,7 @@
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SdkConstant;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
@@ -97,6 +98,22 @@
/** @hide */
public static final int AUTOCLICK_DELAY_DEFAULT = 600;
+ /**
+ * Activity action: Launch UI to manage which accessibility service or feature is assigned
+ * to the navigation bar Accessibility button.
+ * <p>
+ * Input: Nothing.
+ * </p>
+ * <p>
+ * Output: Nothing.
+ * </p>
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_CHOOSE_ACCESSIBILITY_BUTTON =
+ "android.intent.action.CHOOSE_ACCESSIBILITY_BUTTON";
+
static final Object sInstanceSync = new Object();
private static AccessibilityManager sInstance;
diff --git a/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
new file mode 100644
index 0000000..ee5d339
--- /dev/null
+++ b/core/java/com/android/internal/app/AccessibilityButtonChooserActivity.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.app;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.BaseAdapter;
+import android.widget.GridView;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * Activity used to display and persist a service or feature target for the Accessibility button.
+ */
+public class AccessibilityButtonChooserActivity extends Activity {
+
+ private static final String MAGNIFICATION_COMPONENT_ID =
+ "com.android.server.accessibility.MagnificationController";
+
+ private AccessibilityButtonTarget mMagnificationTarget = null;
+
+ private List<AccessibilityButtonTarget> mTargets = null;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.accessibility_button_chooser);
+
+ String component = Settings.Secure.getString(getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
+ if (TextUtils.isEmpty(component)) {
+ TextView prompt = (TextView) findViewById(R.id.accessibility_button_prompt);
+ prompt.setVisibility(View.VISIBLE);
+ }
+
+ mMagnificationTarget = new AccessibilityButtonTarget(this, MAGNIFICATION_COMPONENT_ID,
+ R.string.accessibility_magnification_chooser_text,
+ R.drawable.resolver_icon_placeholder);
+
+ mTargets = getServiceAccessibilityButtonTargets(this);
+ if (Settings.Secure.getInt(getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0) == 1) {
+ mTargets.add(mMagnificationTarget);
+ }
+
+ if (mTargets.size() < 2) {
+ // Why are we here?
+ finish();
+ }
+
+ GridView gridview = (GridView) findViewById(R.id.accessibility_button_chooser_grid);
+ gridview.setAdapter(new TargetAdapter());
+ gridview.setOnItemClickListener((parent, view, position, id) -> {
+ onTargetSelected(mTargets.get(position));
+ });
+ }
+
+ private static List<AccessibilityButtonTarget> getServiceAccessibilityButtonTargets(
+ @NonNull Context context) {
+ AccessibilityManager ams = (AccessibilityManager) context.getSystemService(
+ Context.ACCESSIBILITY_SERVICE);
+ List<AccessibilityServiceInfo> services = ams.getEnabledAccessibilityServiceList(
+ AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
+ if (services == null) {
+ return Collections.emptyList();
+ }
+
+ ArrayList<AccessibilityButtonTarget> targets = new ArrayList<>(services.size());
+ for (AccessibilityServiceInfo info : services) {
+ if ((info.flags & AccessibilityServiceInfo.FLAG_REQUEST_ACCESSIBILITY_BUTTON) != 0) {
+ targets.add(new AccessibilityButtonTarget(context, info));
+ }
+ }
+
+ return targets;
+ }
+
+ private void onTargetSelected(AccessibilityButtonTarget target) {
+ Settings.Secure.putString(getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, target.getId());
+ finish();
+ }
+
+ private class TargetAdapter extends BaseAdapter {
+ @Override
+ public int getCount() {
+ return mTargets.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return null;
+ }
+
+ @Override
+ public long getItemId(int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ LayoutInflater inflater = AccessibilityButtonChooserActivity.this.getLayoutInflater();
+ View root = inflater.inflate(R.layout.accessibility_button_chooser_item, parent, false);
+ final AccessibilityButtonTarget target = mTargets.get(position);
+ ImageView iconView = root.findViewById(R.id.accessibility_button_target_icon);
+ TextView labelView = root.findViewById(R.id.accessibility_button_target_label);
+ iconView.setImageDrawable(target.getDrawable());
+ labelView.setText(target.getLabel());
+ return root;
+ }
+ }
+
+ private static class AccessibilityButtonTarget {
+ public String mId;
+ public CharSequence mLabel;
+ public Drawable mDrawable;
+
+ public AccessibilityButtonTarget(@NonNull Context context,
+ @NonNull AccessibilityServiceInfo serviceInfo) {
+ this.mId = serviceInfo.getComponentName().flattenToString();
+ this.mLabel = serviceInfo.getResolveInfo().loadLabel(context.getPackageManager());
+ this.mDrawable = serviceInfo.getResolveInfo().loadIcon(context.getPackageManager());
+ }
+
+ public AccessibilityButtonTarget(Context context, @NonNull String id, int labelResId,
+ int iconRes) {
+ this.mId = id;
+ this.mLabel = context.getText(labelResId);
+ this.mDrawable = context.getDrawable(iconRes);
+ }
+
+ public String getId() {
+ return mId;
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public Drawable getDrawable() {
+ return mDrawable;
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8abd022..8721f34 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3344,6 +3344,19 @@
<category android:name="android.intent.category.VOICE" />
</intent-filter>
</activity>
+ <activity android:name="com.android.internal.app.AccessibilityButtonChooserActivity"
+ android:theme="@style/Theme.DeviceDefault.Resolver"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true"
+ android:documentLaunchMode="never"
+ android:relinquishTaskIdentity="true"
+ android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation|keyboard|keyboardHidden"
+ android:process=":ui">
+ <intent-filter>
+ <action android:name="android.intent.action.CHOOSE_ACCESSIBILITY_BUTTON" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
<activity android:name="com.android.internal.app.IntentForwarderActivity"
android:finishOnCloseSystemDialogs="true"
android:theme="@style/Theme.NoDisplay"
diff --git a/core/res/res/layout/accessibility_button_chooser.xml b/core/res/res/layout/accessibility_button_chooser.xml
new file mode 100644
index 0000000..0ef785f
--- /dev/null
+++ b/core/res/res/layout/accessibility_button_chooser.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+* Copyright 2017, The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+-->
+<com.android.internal.widget.ResolverDrawerLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:maxWidth="@dimen/resolver_max_width"
+ android:maxCollapsedHeight="256dp"
+ android:maxCollapsedHeightSmall="56dp"
+ android:id="@id/contentPanel">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:background="?attr/colorBackground"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:minHeight="56dp"
+ android:textAppearance="?attr/textAppearanceMedium"
+ android:text="@string/accessibility_button_prompt_text"
+ android:gravity="start|center_vertical"
+ android:layout_alignParentStart="true"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"/>
+
+ <GridView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/accessibility_button_chooser_grid"
+ android:columnWidth="90dp"
+ android:numColumns="auto_fit"
+ android:verticalSpacing="10dp"
+ android:horizontalSpacing="10dp"
+ android:stretchMode="columnWidth"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:gravity="center"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/accessibility_button_prompt"
+ android:layout_alwaysShow="true"
+ android:textAppearance="?attr/textAppearanceMedium"
+ android:text="@string/accessibility_button_instructional_text"
+ android:gravity="start|center_vertical"
+ android:paddingStart="?attr/dialogPreferredPadding"
+ android:paddingEnd="?attr/dialogPreferredPadding"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:visibility="gone"/>
+ </LinearLayout>
+</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/layout/accessibility_button_chooser_item.xml b/core/res/res/layout/accessibility_button_chooser_item.xml
new file mode 100644
index 0000000..76a9308
--- /dev/null
+++ b/core/res/res/layout/accessibility_button_chooser_item.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2017, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:minWidth="80dp"
+ android:gravity="center"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:background="?attr/selectableItemBackgroundBorderless">
+
+ <ImageView android:id="@+id/accessibility_button_target_icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginLeft="3dp"
+ android:layout_marginRight="3dp"
+ android:layout_marginBottom="3dp"
+ android:scaleType="fitCenter"/>
+
+ <TextView android:id="@+id/accessibility_button_target_label"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginRight="4dp"
+ android:textAppearance="?attr/textAppearanceSmall"
+ android:textColor="?attr/textColorPrimary"
+ android:textSize="12sp"
+ android:fontFamily="sans-serif-condensed"
+ android:gravity="top|center_horizontal"
+ android:minLines="2"
+ android:maxLines="2"
+ android:ellipsize="marquee"/>
+</LinearLayout>
+
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 566ba02..868e256 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3958,6 +3958,15 @@
<string name="accessibility_shortcut_disabling_service">Accessibility Shortcut turned
<xliff:g id="service_name" example="TalkBack">%1$s</xliff:g> off</string>
+ <!-- Text appearing in a prompt at the top of UI allowing the user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. -->
+ <string name="accessibility_button_prompt_text">Choose a feature to use when you tap the Accessibility button:</string>
+
+ <!-- Text describing how to display UI allowing a user to select a target service or feature to be assigned to the Accessibility button in the navigation bar. -->
+ <string name="accessibility_button_instructional_text">To change features, touch & hold the Accessibility button.</string>
+
+ <!-- Text used to describe system navigation features, shown within a UI allowing a user to assign system magnification features to the Accessibility button in the navigation bar. -->
+ <string name="accessibility_magnification_chooser_text">Magnification</string>
+
<!-- Text spoken when the current user is switched if accessibility is enabled. [CHAR LIMIT=none] -->
<string name="user_switched">Current user <xliff:g id="name" example="Bob">%1$s</xliff:g>.</string>
<!-- Message shown when switching to a user [CHAR LIMIT=none] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index f4d490a..b23c96c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2841,6 +2841,15 @@
<java-symbol type="string" name="leave_accessibility_shortcut_on" />
<java-symbol type="string" name="config_defaultAccessibilityService" />
+ <!-- Accessibility Button -->
+ <java-symbol type="layout" name="accessibility_button_chooser" />
+ <java-symbol type="layout" name="accessibility_button_chooser_item" />
+ <java-symbol type="id" name="accessibility_button_chooser_grid" />
+ <java-symbol type="id" name="accessibility_button_prompt" />
+ <java-symbol type="id" name="accessibility_button_target_icon" />
+ <java-symbol type="id" name="accessibility_button_target_label" />
+ <java-symbol type="string" name="accessibility_magnification_chooser_text" />
+
<!-- com.android.internal.widget.RecyclerView -->
<java-symbol type="id" name="item_touch_helper_previous_elevation"/>
<java-symbol type="dimen" name="item_touch_helper_max_drag_scroll_per_frame"/>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 5fb642f..1f03024 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -557,9 +557,9 @@
}
private boolean onAccessibilityLongClick(View v) {
- // TODO(b/34720082): Target service selection via long click
- android.widget.Toast.makeText(getContext(), "Service selection coming soon...",
- android.widget.Toast.LENGTH_LONG).show();
+ Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ v.getContext().startActivity(intent);
return true;
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3666763..05c6592 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -70,6 +70,7 @@
import android.os.UserManager;
import android.os.UserManagerInternal;
import android.provider.Settings;
+import android.provider.SettingsStringUtil;
import android.provider.SettingsStringUtil.ComponentNameSet;
import android.provider.SettingsStringUtil.SettingStringHelper;
import android.text.TextUtils;
@@ -100,7 +101,6 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
import com.android.internal.util.IntPair;
import com.android.server.LocalServices;
@@ -1154,17 +1154,55 @@
private void notifyAccessibilityButtonClickedLocked() {
final UserState state = getCurrentUserStateLocked();
- if (state.mIsNavBarMagnificationEnabled) {
- mMainHandler.obtainMessage(
- MainHandler.MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER).sendToTarget();
- } else {
- for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
- final Service service = state.mBoundServices.get(i);
- // TODO(b/34720082): Only notify a single user-defined service
- if (service.mRequestAccessibilityButton) {
- service.notifyAccessibilityButtonClickedLocked();
+
+ int potentialTargets = state.mIsNavBarMagnificationEnabled ? 1 : 0;
+ for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+ final Service service = state.mBoundServices.get(i);
+ if (service.mRequestAccessibilityButton) {
+ potentialTargets++;
+ }
+ }
+
+ if (potentialTargets == 0) {
+ return;
+ }
+ if (potentialTargets == 1) {
+ if (state.mIsNavBarMagnificationEnabled) {
+ mMainHandler.obtainMessage(
+ MainHandler.MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER).sendToTarget();
+ return;
+ } else {
+ for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+ final Service service = state.mBoundServices.get(i);
+ if (service.mRequestAccessibilityButton) {
+ service.notifyAccessibilityButtonClickedLocked();
+ return;
+ }
}
}
+ } else {
+ if (state.mServiceAssignedToAccessibilityButton == null
+ && !state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ mMainHandler.obtainMessage(
+ MainHandler.MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER).sendToTarget();
+ } else if (state.mIsNavBarMagnificationEnabled
+ && state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ mMainHandler.obtainMessage(
+ MainHandler.MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER).sendToTarget();
+ return;
+ } else {
+ for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
+ final Service service = state.mBoundServices.get(i);
+ if (service.mRequestAccessibilityButton && (service.mComponentName.equals(
+ state.mServiceAssignedToAccessibilityButton))) {
+ service.notifyAccessibilityButtonClickedLocked();
+ return;
+ }
+ }
+ }
+ // The user may have turned off the assigned service or feature
+ mMainHandler.obtainMessage(
+ MainHandler.MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER).sendToTarget();
}
}
@@ -1534,6 +1572,12 @@
}
}
+ private void showAccessibilityButtonTargetSelection() {
+ Intent intent = new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ mContext.startActivity(intent);
+ }
+
private void scheduleNotifyClientsOfServicesStateChange(UserState userState) {
mMainHandler.obtainMessage(MainHandler.MSG_SEND_SERVICES_STATE_CHANGED_TO_CLIENTS,
userState.mUserId).sendToTarget();
@@ -1681,6 +1725,7 @@
scheduleUpdateInputFilter(userState);
scheduleUpdateClientsIfNeededLocked(userState);
updateRelevantEventsLocked(userState);
+ updateAccessibilityButtonTargets(userState);
}
private void updateAccessibilityFocusBehaviorLocked(UserState userState) {
@@ -1794,6 +1839,7 @@
somethingChanged |= readMagnificationEnabledSettingsLocked(userState);
somethingChanged |= readAutoclickEnabledSettingLocked(userState);
somethingChanged |= readAccessibilityShortcutSettingLocked(userState);
+ somethingChanged |= readAccessibilityButtonSettingsLocked(userState);
return somethingChanged;
}
@@ -1928,6 +1974,37 @@
return true;
}
+ private boolean readAccessibilityButtonSettingsLocked(UserState userState) {
+ String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId);
+ if (TextUtils.isEmpty(componentId)) {
+ if ((userState.mServiceAssignedToAccessibilityButton == null)
+ && !userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ return false;
+ }
+ userState.mServiceAssignedToAccessibilityButton = null;
+ userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+ return true;
+ }
+
+ if (componentId.equals(MagnificationController.class.getName())) {
+ if (userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+ return false;
+ }
+ userState.mServiceAssignedToAccessibilityButton = null;
+ userState.mIsNavBarMagnificationAssignedToAccessibilityButton = true;
+ return true;
+ }
+
+ ComponentName componentName = ComponentName.unflattenFromString(componentId);
+ if (componentName.equals(userState.mServiceAssignedToAccessibilityButton)) {
+ return false;
+ }
+ userState.mServiceAssignedToAccessibilityButton = componentName;
+ userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+ return true;
+ }
+
/**
* Check if the service that will be enabled by the shortcut is installed. If it isn't,
* clear the value and the associated setting so a sideloaded service can't spoof the
@@ -2138,6 +2215,22 @@
}
}
+ private void updateAccessibilityButtonTargets(UserState userState) {
+ final List<Service> services;
+ synchronized (mLock) {
+ services = userState.mBoundServices;
+ int numServices = services.size();
+ for (int i = 0; i < numServices; i++) {
+ final Service service = services.get(i);
+ if (service.mRequestAccessibilityButton) {
+ boolean available = service.mComponentName.equals(
+ userState.mServiceAssignedToAccessibilityButton);
+ service.notifyAccessibilityButtonAvailabilityChangedLocked(available);
+ }
+ }
+ }
+ }
+
@GuardedBy("mLock")
private MagnificationSpec getCompatibleMagnificationSpecLocked(int windowId) {
IBinder windowToken = mGlobalWindowTokens.get(windowId);
@@ -2212,7 +2305,7 @@
* Disables accessibility service specified by {@param componentName} for the {@param userId}.
*/
private void disableAccessibilityServiceLocked(ComponentName componentName, int userId) {
- final SettingStringHelper setting =
+ final SettingsStringUtil.SettingStringHelper setting =
new SettingStringHelper(
mContext.getContentResolver(),
Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
@@ -2342,6 +2435,7 @@
public static final int MSG_UPDATE_FINGERPRINT = 11;
public static final int MSG_SEND_RELEVANT_EVENTS_CHANGED_TO_CLIENTS = 12;
public static final int MSG_SEND_ACCESSIBILITY_BUTTON_TO_INPUT_FILTER = 13;
+ public static final int MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER = 14;
public MainHandler(Looper looper) {
super(looper);
@@ -2435,6 +2529,10 @@
mInputFilter.notifyAccessibilityButtonClicked();
}
}
+ } break;
+
+ case MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER: {
+ showAccessibilityButtonTargetSelection();
}
}
}
@@ -4813,6 +4911,8 @@
public int mSoftKeyboardShowMode = 0;
public boolean mIsAccessibilityButtonAvailable;
+ public boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
+ public ComponentName mServiceAssignedToAccessibilityButton;
public boolean mIsTouchExplorationEnabled;
public boolean mIsTextHighContrastEnabled;
@@ -4888,6 +4988,8 @@
mIsEnhancedWebAccessibilityEnabled = false;
mIsDisplayMagnificationEnabled = false;
mIsNavBarMagnificationEnabled = false;
+ mServiceAssignedToAccessibilityButton = null;
+ mIsNavBarMagnificationAssignedToAccessibilityButton = false;
mIsAutoclickEnabled = false;
mSoftKeyboardShowMode = 0;
@@ -4953,6 +5055,9 @@
private final Uri mAccessibilityShortcutServiceIdUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_SHORTCUT_TARGET_SERVICE);
+ private final Uri mAccessibilityButtonComponentIdUri = Settings.Secure.getUriFor(
+ Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT);
+
public AccessibilityContentObserver(Handler handler) {
super(handler);
}
@@ -4985,6 +5090,8 @@
mAccessibilitySoftKeyboardModeUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mAccessibilityShortcutServiceIdUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
+ mAccessibilityButtonComponentIdUri, false, this, UserHandle.USER_ALL);
}
@Override
@@ -5042,6 +5149,10 @@
if (readAccessibilityShortcutSettingLocked(userState)) {
onUserStateChangedLocked(userState);
}
+ } else if (mAccessibilityButtonComponentIdUri.equals(uri)) {
+ if (readAccessibilityButtonSettingsLocked(userState)) {
+ onUserStateChangedLocked(userState);
+ }
}
}
}