summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt7
-rw-r--r--core/java/android/view/accessibility/AccessibilityManager.java119
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManager.aidl2
-rw-r--r--core/java/android/view/accessibility/IAccessibilityManagerClient.aidl2
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java51
-rw-r--r--services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java22
6 files changed, 201 insertions, 2 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index 2a958933713c..8ecf38abf85d 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -52687,12 +52687,14 @@ package android.view.accessibility {
method public void addAudioDescriptionRequestedChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.accessibility.AccessibilityManager.AudioDescriptionRequestedChangeListener);
method public boolean addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
method public void addTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener, @Nullable android.os.Handler);
+ method public void addUiContrastChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.accessibility.AccessibilityManager.UiContrastChangeListener);
method @ColorInt public int getAccessibilityFocusColor();
method public int getAccessibilityFocusStrokeWidth();
method @Deprecated public java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int);
method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList();
method public int getRecommendedTimeoutMillis(int, int);
+ method @FloatRange(from=-1.0F, to=1.0f) public float getUiContrast();
method public void interrupt();
method public static boolean isAccessibilityButtonSupported();
method public boolean isAudioDescriptionRequested();
@@ -52704,6 +52706,7 @@ package android.view.accessibility {
method public boolean removeAccessibilityStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
method public boolean removeAudioDescriptionRequestedChangeListener(@NonNull android.view.accessibility.AccessibilityManager.AudioDescriptionRequestedChangeListener);
method public boolean removeTouchExplorationStateChangeListener(@NonNull android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
+ method public void removeUiContrastChangeListener(@NonNull android.view.accessibility.AccessibilityManager.UiContrastChangeListener);
method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
field public static final int FLAG_CONTENT_CONTROLS = 4; // 0x4
field public static final int FLAG_CONTENT_ICONS = 1; // 0x1
@@ -52726,6 +52729,10 @@ package android.view.accessibility {
method public void onTouchExplorationStateChanged(boolean);
}
+ public static interface AccessibilityManager.UiContrastChangeListener {
+ method public void onUiContrastChanged(@FloatRange(from=-1.0F, to=1.0f) float);
+ }
+
public class AccessibilityNodeInfo implements android.os.Parcelable {
ctor public AccessibilityNodeInfo();
ctor public AccessibilityNodeInfo(@NonNull android.view.View);
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 423c560d5c57..9abbba923a66 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -25,6 +25,7 @@ import android.accessibilityservice.AccessibilityServiceInfo.FeedbackType;
import android.accessibilityservice.AccessibilityShortcutInfo;
import android.annotation.CallbackExecutor;
import android.annotation.ColorInt;
+import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -75,6 +76,7 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -138,6 +140,21 @@ public final class AccessibilityManager {
public static final int AUTOCLICK_DELAY_DEFAULT = 600;
/**
+ * The contrast is defined as a float in [-1, 1], with a default value of 0.
+ * @hide
+ */
+ public static final float CONTRAST_MIN_VALUE = -1f;
+
+ /** @hide */
+ public static final float CONTRAST_MAX_VALUE = 1f;
+
+ /** @hide */
+ public static final float CONTRAST_DEFAULT_VALUE = 0f;
+
+ /** @hide */
+ public static final float CONTRAST_NOT_SET = Float.MIN_VALUE;
+
+ /**
* Activity action: Launch UI to manage which accessibility service or feature is assigned
* to the navigation bar Accessibility button.
* <p>
@@ -246,6 +263,8 @@ public final class AccessibilityManager {
@UnsupportedAppUsage(trackingBug = 123768939L)
boolean mIsHighTextContrastEnabled;
+ private float mUiContrast;
+
boolean mIsAudioDescriptionByDefaultRequested;
// accessibility tracing state
@@ -270,6 +289,9 @@ public final class AccessibilityManager {
private final ArrayMap<HighTextContrastChangeListener, Handler>
mHighTextContrastStateChangeListeners = new ArrayMap<>();
+ private final ArrayMap<UiContrastChangeListener, Executor>
+ mUiContrastChangeListeners = new ArrayMap<>();
+
private final ArrayMap<AccessibilityServicesStateChangeListener, Executor>
mServicesStateChangeListeners = new ArrayMap<>();
@@ -336,7 +358,7 @@ public final class AccessibilityManager {
*
* @param manager The manager that is calling back
*/
- void onAccessibilityServicesStateChanged(@NonNull AccessibilityManager manager);
+ void onAccessibilityServicesStateChanged(@NonNull AccessibilityManager manager);
}
/**
@@ -358,6 +380,21 @@ public final class AccessibilityManager {
}
/**
+ * Listener for the UI contrast. To listen for changes to
+ * the UI contrast on the device, implement this interface and
+ * register it with the system by calling {@link #addUiContrastChangeListener}.
+ */
+ public interface UiContrastChangeListener {
+
+ /**
+ * Called when the color contrast enabled state changes.
+ *
+ * @param uiContrast The color contrast as in {@link #getUiContrast}
+ */
+ void onUiContrastChanged(@FloatRange(from = -1.0f, to = 1.0f) float uiContrast);
+ }
+
+ /**
* Listener for the audio description by default state. To listen for
* changes to the audio description by default state on the device,
* implement this interface and register it with the system by calling
@@ -471,6 +508,16 @@ public final class AccessibilityManager {
updateFocusAppearanceLocked(strokeWidth, color);
}
}
+
+ @Override
+ public void setUiContrast(float contrast) {
+ synchronized (mLock) {
+ // if value changed in the settings, update the cached value and notify listeners
+ if (Math.abs(mUiContrast - contrast) < 1e-10) return;
+ mUiContrast = contrast;
+ }
+ mHandler.obtainMessage(MyCallback.MSG_NOTIFY_CONTRAST_CHANGED).sendToTarget();
+ }
};
/**
@@ -641,7 +688,7 @@ public final class AccessibilityManager {
/**
* Returns if the high text contrast in the system is enabled.
* <p>
- * <strong>Note:</strong> You need to query this only if you application is
+ * <strong>Note:</strong> You need to query this only if your application is
* doing its own rendering and does not rely on the platform rendering pipeline.
* </p>
*
@@ -661,6 +708,24 @@ public final class AccessibilityManager {
}
/**
+ * Returns the color contrast for the user.
+ * <p>
+ * <strong>Note:</strong> You need to query this only if your application is
+ * doing its own rendering and does not rely on the platform rendering pipeline.
+ * </p>
+ * @return The color contrast, float in [-1, 1] where
+ * 0 corresponds to the default contrast
+ * -1 corresponds to the minimum contrast that the user can set
+ * 1 corresponds to the maximum contrast that the user can set
+ */
+ @FloatRange(from = -1.0f, to = 1.0f)
+ public float getUiContrast() {
+ synchronized (mLock) {
+ return mUiContrast;
+ }
+ }
+
+ /**
* Sends an {@link AccessibilityEvent}.
*
* @param event The event to send.
@@ -1240,6 +1305,35 @@ public final class AccessibilityManager {
}
/**
+ * Registers a {@link UiContrastChangeListener} for the current user.
+ *
+ * @param executor The executor on which the listener should be called back.
+ * @param listener The listener.
+ */
+ public void addUiContrastChangeListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull UiContrastChangeListener listener) {
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(listener);
+ synchronized (mLock) {
+ mUiContrastChangeListeners.put(listener, executor);
+ }
+ }
+
+ /**
+ * Unregisters a {@link UiContrastChangeListener} for the current user.
+ * If the listener was not registered, does nothing and returns.
+ *
+ * @param listener The listener to unregister.
+ */
+ public void removeUiContrastChangeListener(@NonNull UiContrastChangeListener listener) {
+ Objects.requireNonNull(listener);
+ synchronized (mLock) {
+ mUiContrastChangeListeners.remove(listener);
+ }
+ }
+
+ /**
* Registers a {@link AudioDescriptionRequestedChangeListener}
* for changes in the audio description by default state of the system.
* The value could be read via {@link #isAudioDescriptionRequested}.
@@ -2004,6 +2098,7 @@ public final class AccessibilityManager {
mRelevantEventTypes = IntPair.second(userStateAndRelevantEvents);
updateUiTimeout(service.getRecommendedTimeoutMillis());
updateFocusAppearanceLocked(service.getFocusStrokeWidth(), service.getFocusColor());
+ mUiContrast = service.getUiContrast();
mService = service;
} catch (RemoteException re) {
Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
@@ -2082,6 +2177,22 @@ public final class AccessibilityManager {
}
/**
+ * Notifies the registered {@link UiContrastChangeListener}s if the value changed.
+ */
+ private void notifyUiContrastChanged() {
+ final ArrayMap<UiContrastChangeListener, Executor> listeners;
+ synchronized (mLock) {
+ listeners = new ArrayMap<>(mUiContrastChangeListeners);
+ }
+
+ listeners.entrySet().forEach(entry -> {
+ UiContrastChangeListener listener = entry.getKey();
+ Executor executor = entry.getValue();
+ executor.execute(() -> listener.onUiContrastChanged(mUiContrast));
+ });
+ }
+
+ /**
* Notifies the registered {@link AudioDescriptionStateChangeListener}s.
*/
private void notifyAudioDescriptionbyDefaultStateChanged() {
@@ -2171,6 +2282,7 @@ public final class AccessibilityManager {
private final class MyCallback implements Handler.Callback {
public static final int MSG_SET_STATE = 1;
+ public static final int MSG_NOTIFY_CONTRAST_CHANGED = 2;
@Override
public boolean handleMessage(Message message) {
@@ -2182,6 +2294,9 @@ public final class AccessibilityManager {
setStateLocked(state);
}
} break;
+ case MSG_NOTIFY_CONTRAST_CHANGED: {
+ notifyUiContrastChanged();
+ }
}
return true;
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 364c7c8e1fb9..c2d899a50d4e 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -118,4 +118,6 @@ interface IAccessibilityManager {
// Used by UiAutomation for tests on the InputFilter
void injectInputEventToInputFilter(in InputEvent event);
+
+ float getUiContrast();
}
diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
index 041399ccb8ec..931f862e581b 100644
--- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl
@@ -31,4 +31,6 @@ oneway interface IAccessibilityManagerClient {
void setRelevantEventTypes(int eventTypes);
void setFocusAppearance(int strokeWidth, int color);
+
+ void setUiContrast(float contrast);
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 3145139cc6bb..5c2f6fc1d6cf 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -26,8 +26,11 @@ import static android.accessibilityservice.AccessibilityTrace.FLAGS_USER_BROADCA
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MAGNIFICATION_CONNECTION;
import static android.accessibilityservice.AccessibilityTrace.FLAGS_WINDOW_MANAGER_INTERNAL;
import static android.provider.Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED;
+import static android.provider.Settings.Secure.CONTRAST_LEVEL;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
+import static android.view.accessibility.AccessibilityManager.CONTRAST_DEFAULT_VALUE;
+import static android.view.accessibility.AccessibilityManager.CONTRAST_NOT_SET;
import static android.view.accessibility.AccessibilityManager.ShortcutType;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_COMPONENT_NAME;
@@ -1899,6 +1902,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return false;
}
+ private boolean readUiContrastLocked(AccessibilityUserState userState) {
+ float contrast = Settings.Secure.getFloatForUser(mContext.getContentResolver(),
+ CONTRAST_LEVEL, CONTRAST_DEFAULT_VALUE, userState.mUserId);
+ if (Math.abs(userState.getUiContrastLocked() - contrast) >= 1e-10) {
+ userState.setUiContrastLocked(contrast);
+ return true;
+ }
+ return false;
+ }
+
/**
* Performs {@link AccessibilityService}s delayed notification. The delay is configurable
* and denotes the period after the last event before notifying the service.
@@ -2565,6 +2578,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
somethingChanged |= readMagnificationModeForDefaultDisplayLocked(userState);
somethingChanged |= readMagnificationCapabilitiesLocked(userState);
somethingChanged |= readMagnificationFollowTypingLocked(userState);
+ somethingChanged |= readUiContrastLocked(userState);
return somethingChanged;
}
@@ -3706,6 +3720,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
return mProxyManager.unregisterProxy(displayId);
}
+ @Override public float getUiContrast() {
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_MANAGER)) {
+ mTraceManager.logTrace(LOG_TAG + ".getUiContrast", FLAGS_ACCESSIBILITY_MANAGER);
+ }
+ synchronized (mLock) {
+ AccessibilityUserState userState = getCurrentUserStateLocked();
+ float contrast = userState.getUiContrastLocked();
+ if (contrast != CONTRAST_NOT_SET) return contrast;
+ readUiContrastLocked(userState);
+ return userState.getUiContrastLocked();
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, LOG_TAG, pw)) return;
@@ -4153,6 +4180,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
private final Uri mMagnificationFollowTypingUri = Settings.Secure.getUriFor(
Settings.Secure.ACCESSIBILITY_MAGNIFICATION_FOLLOW_TYPING_ENABLED);
+ private final Uri mUiContrastUri = Settings.Secure.getUriFor(
+ CONTRAST_LEVEL);
+
public AccessibilityContentObserver(Handler handler) {
super(handler);
}
@@ -4193,6 +4223,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
mMagnificationCapabilityUri, false, this, UserHandle.USER_ALL);
contentResolver.registerContentObserver(
mMagnificationFollowTypingUri, false, this, UserHandle.USER_ALL);
+ contentResolver.registerContentObserver(
+ mUiContrastUri, false, this, UserHandle.USER_ALL);
}
@Override
@@ -4262,6 +4294,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
}
} else if (mMagnificationFollowTypingUri.equals(uri)) {
readMagnificationFollowTypingLocked(userState);
+ } else if (mUiContrastUri.equals(uri)) {
+ if (readUiContrastLocked(userState)) {
+ updateUiContrastLocked(userState);
+ }
}
}
}
@@ -4551,7 +4587,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
userState.getFocusColorLocked());
}));
});
+ }
+ private void updateUiContrastLocked(AccessibilityUserState userState) {
+ if (userState.mUserId != mCurrentUserId) {
+ return;
+ }
+ if (mTraceManager.isA11yTracingEnabledForTypes(FLAGS_ACCESSIBILITY_SERVICE_CLIENT)) {
+ mTraceManager.logTrace(LOG_TAG + ".updateUiContrastLocked",
+ FLAGS_ACCESSIBILITY_SERVICE_CLIENT, "userState=" + userState);
+ }
+ float contrast = userState.getUiContrastLocked();
+ mMainHandler.post(() -> {
+ broadcastToClients(userState, ignoreRemoteException(client -> {
+ client.mCallback.setUiContrast(contrast);
+ }));
+ });
}
public AccessibilityTraceManager getTraceManager() {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
index 0db169fd76c3..43730fce0cb7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -26,6 +26,8 @@ import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_
import static android.provider.Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_NONE;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_BUTTON;
import static android.view.accessibility.AccessibilityManager.ACCESSIBILITY_SHORTCUT_KEY;
+import static android.view.accessibility.AccessibilityManager.CONTRAST_DEFAULT_VALUE;
+import static android.view.accessibility.AccessibilityManager.CONTRAST_NOT_SET;
import static android.view.accessibility.AccessibilityManager.ShortcutType;
import static com.android.internal.accessibility.AccessibilityShortcutController.MAGNIFICATION_CONTROLLER_NAME;
@@ -143,6 +145,8 @@ class AccessibilityUserState {
private final int mFocusStrokeWidthDefaultValue;
// The default value of the focus color.
private final int mFocusColorDefaultValue;
+ /** The color contrast in [-1, 1] */
+ private float mUiContrast = CONTRAST_DEFAULT_VALUE;
private Context mContext;
@@ -217,6 +221,7 @@ class AccessibilityUserState {
mFocusStrokeWidth = mFocusStrokeWidthDefaultValue;
mFocusColor = mFocusColorDefaultValue;
mMagnificationFollowTypingEnabled = true;
+ mUiContrast = CONTRAST_NOT_SET;
}
void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
@@ -983,6 +988,7 @@ class AccessibilityUserState {
return mFocusColor;
}
+
/**
* Sets the stroke width and color of the focus rectangle.
*
@@ -1008,4 +1014,20 @@ class AccessibilityUserState {
}
return false;
}
+
+ /**
+ * Get the color contrast
+ * @return color contrast in [-1, 1]
+ */
+ public float getUiContrastLocked() {
+ return mUiContrast;
+ }
+
+ /**
+ * Set the color contrast
+ * @param contrast the new color contrast in [-1, 1]
+ */
+ public void setUiContrastLocked(float contrast) {
+ mUiContrast = contrast;
+ }
}