From c7575771bdf40bc9c90697b7ee18beea69923685 Mon Sep 17 00:00:00 2001 From: Aurélien Pomini Date: Fri, 24 Feb 2023 12:29:41 +0000 Subject: Revert "Add API to get/listen to the color contrast setting" Revert submission 20579497-color_contrast_api Reason for revert: the contrast API will move to UiModeManager Reverted changes: /q/submissionid:20579497-color_contrast_api Bug: 259091608 Test: atest AccessibilityManagerTest Change-Id: I2a6c4f5313a0328093ca8125302e43b20dbdd352 --- core/api/current.txt | 7 -- .../view/accessibility/AccessibilityManager.java | 119 +-------------------- .../view/accessibility/IAccessibilityManager.aidl | 2 - .../accessibility/IAccessibilityManagerClient.aidl | 2 - .../accessibility/AccessibilityManagerService.java | 51 --------- .../accessibility/AccessibilityUserState.java | 22 ---- 6 files changed, 2 insertions(+), 201 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 5af80797c91f..fc53bad95d0f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -54145,14 +54145,12 @@ 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 getAccessibilityServiceList(); method public java.util.List getEnabledAccessibilityServiceList(int); method public java.util.List 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(); @@ -54164,7 +54162,6 @@ 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 @@ -54187,10 +54184,6 @@ 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 9504852f6e98..5ad2476930f1 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -25,7 +25,6 @@ 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; @@ -76,7 +75,6 @@ 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; /** @@ -139,21 +137,6 @@ public final class AccessibilityManager { /** @hide */ 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. @@ -288,8 +271,6 @@ public final class AccessibilityManager { @UnsupportedAppUsage(trackingBug = 123768939L) boolean mIsHighTextContrastEnabled; - private float mUiContrast; - boolean mIsAudioDescriptionByDefaultRequested; // accessibility tracing state @@ -314,9 +295,6 @@ public final class AccessibilityManager { private final ArrayMap mHighTextContrastStateChangeListeners = new ArrayMap<>(); - private final ArrayMap - mUiContrastChangeListeners = new ArrayMap<>(); - private final ArrayMap mServicesStateChangeListeners = new ArrayMap<>(); @@ -390,7 +368,7 @@ public final class AccessibilityManager { * * @param manager The manager that is calling back */ - void onAccessibilityServicesStateChanged(@NonNull AccessibilityManager manager); + void onAccessibilityServicesStateChanged(@NonNull AccessibilityManager manager); } /** @@ -411,21 +389,6 @@ public final class AccessibilityManager { void onHighTextContrastStateChanged(boolean enabled); } - /** - * 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, @@ -540,16 +503,6 @@ 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(); - } }; /** @@ -720,7 +673,7 @@ public final class AccessibilityManager { /** * Returns if the high text contrast in the system is enabled. *

- * Note: You need to query this only if your application is + * Note: You need to query this only if you application is * doing its own rendering and does not rely on the platform rendering pipeline. *

* @@ -739,24 +692,6 @@ public final class AccessibilityManager { } } - /** - * Returns the color contrast for the user. - *

- * Note: You need to query this only if your application is - * doing its own rendering and does not rely on the platform rendering pipeline. - *

- * @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}. * @@ -1345,35 +1280,6 @@ 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. @@ -2232,7 +2138,6 @@ 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); @@ -2310,22 +2215,6 @@ public final class AccessibilityManager { } } - /** - * Notifies the registered {@link UiContrastChangeListener}s if the value changed. - */ - private void notifyUiContrastChanged() { - final ArrayMap 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. */ @@ -2416,7 +2305,6 @@ 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) { @@ -2428,9 +2316,6 @@ 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 1302421d0ce2..c8280583f35d 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -120,8 +120,6 @@ interface IAccessibilityManager { // Used by UiAutomation for tests on the InputFilter void injectInputEventToInputFilter(in InputEvent event); - float getUiContrast(); - boolean startFlashNotificationSequence(String opPkg, int reason, IBinder token); boolean stopFlashNotificationSequence(String opPkg); boolean startFlashNotificationEvent(String opPkg, int reason, String reasonPkg); diff --git a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl index 931f862e581b..041399ccb8ec 100644 --- a/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManagerClient.aidl @@ -31,6 +31,4 @@ 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 7fba72b74e8d..6503595f2f62 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -26,11 +26,8 @@ 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.FlashNotificationReason; import static android.view.accessibility.AccessibilityManager.ShortcutType; @@ -1997,16 +1994,6 @@ 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. @@ -2676,7 +2663,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub somethingChanged |= readMagnificationCapabilitiesLocked(userState); somethingChanged |= readMagnificationFollowTypingLocked(userState); somethingChanged |= readAlwaysOnMagnificationLocked(userState); - somethingChanged |= readUiContrastLocked(userState); return somethingChanged; } @@ -3851,19 +3837,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mProxyManager.isProxyed(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 boolean startFlashNotificationSequence(String opPkg, @FlashNotificationReason int reason, IBinder token) { @@ -4350,9 +4323,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final Uri mAlwaysOnMagnificationUri = Settings.Secure.getUriFor( Settings.Secure.ACCESSIBILITY_MAGNIFICATION_ALWAYS_ON_ENABLED); - private final Uri mUiContrastUri = Settings.Secure.getUriFor( - CONTRAST_LEVEL); - public AccessibilityContentObserver(Handler handler) { super(handler); } @@ -4395,8 +4365,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mMagnificationFollowTypingUri, false, this, UserHandle.USER_ALL); contentResolver.registerContentObserver( mAlwaysOnMagnificationUri, false, this, UserHandle.USER_ALL); - contentResolver.registerContentObserver( - mUiContrastUri, false, this, UserHandle.USER_ALL); } @Override @@ -4468,10 +4436,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub readMagnificationFollowTypingLocked(userState); } else if (mAlwaysOnMagnificationUri.equals(uri)) { readAlwaysOnMagnificationLocked(userState); - } else if (mUiContrastUri.equals(uri)) { - if (readUiContrastLocked(userState)) { - updateUiContrastLocked(userState); - } } } } @@ -4788,22 +4752,7 @@ 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 1c9ce3c82735..3b169f8f715e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -26,8 +26,6 @@ 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; @@ -147,8 +145,6 @@ 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; @@ -224,7 +220,6 @@ class AccessibilityUserState { mFocusColor = mFocusColorDefaultValue; mMagnificationFollowTypingEnabled = true; mAlwaysOnMagnificationEnabled = false; - mUiContrast = CONTRAST_NOT_SET; } void addServiceLocked(AccessibilityServiceConnection serviceConnection) { @@ -1001,7 +996,6 @@ class AccessibilityUserState { return mFocusColor; } - /** * Sets the stroke width and color of the focus rectangle. * @@ -1027,20 +1021,4 @@ 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; - } } -- cgit v1.2.3-59-g8ed1b From cbbc772a41d20645ae434d74c482f3f4ad377e2c Mon Sep 17 00:00:00 2001 From: Aurélien Pomini Date: Wed, 22 Feb 2023 14:40:06 +0000 Subject: Add the contrast API to UiModeManager Part of the move of the API from AccessibilityManager to UiModeManager. The logic remains unchained. a new AIDL, IUiModeManagerCallback, was created to let the service notify all UiModeManagers about contrast changes, similarly to what was done in AccessibilityManager Constants (min, max, default contrast) have been moved to a new static class "ContrastUtils" inside "UiModeManager". To avoid creating too many CLs, a few new hidden constants/utils have been added to "ContrastUtils", even though this is not part of the move. Test: atest UiModeManagerTest Bug: 259091608 Change-Id: I0c1cf5accc8b301ea90b81cdc20d5262eef04a8b --- core/api/current.txt | 7 + core/java/android/app/IUiModeManager.aidl | 11 ++ core/java/android/app/IUiModeManagerCallback.aidl | 27 ++++ core/java/android/app/UiModeManager.java | 171 ++++++++++++++++++++- .../com/android/server/UiModeManagerService.java | 67 ++++++++ 5 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 core/java/android/app/IUiModeManagerCallback.aidl diff --git a/core/api/current.txt b/core/api/current.txt index fc53bad95d0f..d0544e2865e9 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -7398,12 +7398,15 @@ package android.app { } public class UiModeManager { + method public void addContrastChangeListener(@NonNull java.util.concurrent.Executor, @NonNull android.app.UiModeManager.ContrastChangeListener); method public void disableCarMode(int); method public void enableCarMode(int); + method @FloatRange(from=-1.0F, to=1.0f) public float getContrast(); method public int getCurrentModeType(); method @NonNull public java.time.LocalTime getCustomNightModeEnd(); method @NonNull public java.time.LocalTime getCustomNightModeStart(); method public int getNightMode(); + method public void removeContrastChangeListener(@NonNull android.app.UiModeManager.ContrastChangeListener); method public void setApplicationNightMode(int); method public void setCustomNightModeEnd(@NonNull java.time.LocalTime); method public void setCustomNightModeStart(@NonNull java.time.LocalTime); @@ -7421,6 +7424,10 @@ package android.app { field public static final int MODE_NIGHT_YES = 2; // 0x2 } + public static interface UiModeManager.ContrastChangeListener { + method public void onContrastChanged(@FloatRange(from=-1.0F, to=1.0f) float); + } + public final class VoiceInteractor { method public android.app.VoiceInteractor.Request getActiveRequest(String); method public android.app.VoiceInteractor.Request[] getActiveRequests(); diff --git a/core/java/android/app/IUiModeManager.aidl b/core/java/android/app/IUiModeManager.aidl index 2242224b2769..2345c27a4587 100644 --- a/core/java/android/app/IUiModeManager.aidl +++ b/core/java/android/app/IUiModeManager.aidl @@ -17,12 +17,18 @@ package android.app; import android.app.IOnProjectionStateChangedListener; +import android.app.IUiModeManagerCallback; /** * Interface used to control special UI modes. * @hide */ interface IUiModeManager { + /** + * @hide + */ + void addCallback(IUiModeManagerCallback callback); + /** * Enables the car mode. Only the system can do this. * @hide @@ -173,4 +179,9 @@ interface IUiModeManager { * Returns currently set projection types. */ int getActiveProjectionTypes(); + + /** + * Returns the contrast for the current user + */ + float getContrast(); } diff --git a/core/java/android/app/IUiModeManagerCallback.aidl b/core/java/android/app/IUiModeManagerCallback.aidl new file mode 100644 index 000000000000..47c18a8bd3cb --- /dev/null +++ b/core/java/android/app/IUiModeManagerCallback.aidl @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2023 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 android.app; + +/** +* Implemented by the UiModeManager client to receive information about changes from the service. +* This is a oneway interface since the server should not block waiting for the client. +* +* @hide +*/ +oneway interface IUiModeManagerCallback { + void notifyContrastChanged(float contrast); +} diff --git a/core/java/android/app/UiModeManager.java b/core/java/android/app/UiModeManager.java index ecab37db4ba7..d90257a69281 100644 --- a/core/java/android/app/UiModeManager.java +++ b/core/java/android/app/UiModeManager.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.CallbackExecutor; +import android.annotation.FloatRange; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; @@ -34,6 +35,7 @@ import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.Log; import android.util.Slog; import com.android.internal.annotations.GuardedBy; @@ -43,10 +45,13 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.ref.WeakReference; import java.time.LocalTime; +import java.util.Comparator; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.Executor; +import java.util.stream.Stream; /** * This class provides access to the system uimode services. These services @@ -72,6 +77,10 @@ import java.util.concurrent.Executor; */ @SystemService(Context.UI_MODE_SERVICE) public class UiModeManager { + + private static final String TAG = "UiModeManager"; + + /** * A listener with a single method that is invoked whenever the packages projecting using the * {@link ProjectionType}s for which it is registered change. @@ -91,7 +100,20 @@ public class UiModeManager { @NonNull Set packageNames); } - private static final String TAG = "UiModeManager"; + /** + * 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 #addContrastChangeListener}. + */ + public interface ContrastChangeListener { + + /** + * Called when the color contrast enabled state changes. + * + * @param contrast The color contrast as in {@link #getContrast} + */ + void onContrastChanged(@FloatRange(from = -1.0f, to = 1.0f) float contrast); + } /** * Broadcast sent when the device's UI has switched to car mode, either @@ -319,6 +341,95 @@ public class UiModeManager { mOnProjectionStateChangedListenerResourceManager = new OnProjectionStateChangedListenerResourceManager(); + /** + * Define constants and conversions between {@link ContrastLevel}s and contrast values. + *

+ * Contrast values are floats defined in [-1, 1], as defined in {@link #getContrast}. + * This is the official data type for contrast; + * all methods from the public API return contrast values. + *

+ *

+ * {@code ContrastLevel}, on the other hand, is an internal-only enumeration of contrasts that + * can be set from the system ui. Each {@code ContrastLevel} has an associated contrast value. + *

+ *

+ * Currently, a user chan chose from three contrast levels: + *

    + *
  • {@link #CONTRAST_LEVEL_STANDARD}, corresponding to the default contrast value 0f
  • + *
  • {@link #CONTRAST_LEVEL_MEDIUM}, corresponding to the contrast value 0.5f
  • + *
  • {@link #CONTRAST_LEVEL_HIGH}, corresponding to the maximum contrast value 1f
  • + *
+ *

+ * + * @hide + */ + public static class ContrastUtils { + + private static final float CONTRAST_MIN_VALUE = -1f; + private static final float CONTRAST_MAX_VALUE = 1f; + public static final float CONTRAST_DEFAULT_VALUE = 0f; + + @IntDef(flag = true, prefix = { "CONTRAST_LEVEL_" }, value = { + CONTRAST_LEVEL_STANDARD, + CONTRAST_LEVEL_MEDIUM, + CONTRAST_LEVEL_HIGH + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ContrastLevel {} + + public static final int CONTRAST_LEVEL_STANDARD = 0; + public static final int CONTRAST_LEVEL_MEDIUM = 1; + public static final int CONTRAST_LEVEL_HIGH = 2; + + private static Stream allContrastLevels() { + return Stream.of(CONTRAST_LEVEL_STANDARD, CONTRAST_LEVEL_MEDIUM, CONTRAST_LEVEL_HIGH); + } + + /** + * Convert a contrast value in [-1, 1] to its associated {@link ContrastLevel} + */ + public static @ContrastLevel int toContrastLevel(float contrast) { + if (contrast < CONTRAST_MIN_VALUE || contrast > CONTRAST_MAX_VALUE) { + throw new IllegalArgumentException("contrast values should be in [-1, 1]"); + } + return allContrastLevels().min(Comparator.comparingDouble(contrastLevel -> + Math.abs(contrastLevel - 2 * contrast))).orElseThrow(); + } + + /** + * Convert a {@link ContrastLevel} to its associated contrast value in [-1, 1] + */ + public static float fromContrastLevel(@ContrastLevel int contrastLevel) { + if (allContrastLevels().noneMatch(level -> level == contrastLevel)) { + throw new IllegalArgumentException("unrecognized contrast level: " + contrastLevel); + } + return contrastLevel / 2f; + } + } + + /** + * Map that stores user provided {@link ContrastChangeListener} callbacks, + * and the executors on which these callbacks should be called. + */ + private final ArrayMap + mContrastChangeListeners = new ArrayMap<>(); + private float mContrast; + + private final IUiModeManagerCallback.Stub mCallback = new IUiModeManagerCallback.Stub() { + @Override + public void notifyContrastChanged(float contrast) { + final ArrayMap listeners; + synchronized (mLock) { + // if value changed in the settings, update the cached value and notify listeners + if (Math.abs(mContrast - contrast) < 1e-10) return; + mContrast = contrast; + listeners = new ArrayMap<>(mContrastChangeListeners); + } + listeners.forEach((listener, executor) -> executor.execute( + () -> listener.onContrastChanged(mContrast))); + } + }; + @UnsupportedAppUsage /*package*/ UiModeManager() throws ServiceNotFoundException { this(null /* context */); @@ -328,6 +439,12 @@ public class UiModeManager { mService = IUiModeManager.Stub.asInterface( ServiceManager.getServiceOrThrow(Context.UI_MODE_SERVICE)); mContext = context; + try { + mService.addCallback(mCallback); + mContrast = mService.getContrast(); + } catch (RemoteException e) { + Log.e(TAG, "Setup failed: UiModeManagerService is dead", e); + } } /** @@ -1067,4 +1184,56 @@ public class UiModeManager { return mExecutorMap.get(innerListener); } } + + /** + * Returns the color contrast for the user. + *

+ * Note: You need to query this only if your application is + * doing its own rendering and does not rely on the material rendering pipeline. + *

+ * @return The color contrast, float in [-1, 1] where + *
    + *
  •   0 corresponds to the default contrast
  • + *
  • -1 corresponds to the minimum contrast
  • + *
  •   1 corresponds to the maximum contrast
  • + *
+ * + * + * + */ + @FloatRange(from = -1.0f, to = 1.0f) + public float getContrast() { + synchronized (mLock) { + return mContrast; + } + } + + /** + * Registers a {@link ContrastChangeListener} for the current user. + * + * @param executor The executor on which the listener should be called back. + * @param listener The listener. + */ + public void addContrastChangeListener( + @NonNull @CallbackExecutor Executor executor, + @NonNull ContrastChangeListener listener) { + Objects.requireNonNull(executor); + Objects.requireNonNull(listener); + synchronized (mLock) { + mContrastChangeListeners.put(listener, executor); + } + } + + /** + * Unregisters a {@link ContrastChangeListener} for the current user. + * If the listener was not registered, does nothing and returns. + * + * @param listener The listener to unregister. + */ + public void removeContrastChangeListener(@NonNull ContrastChangeListener listener) { + Objects.requireNonNull(listener); + synchronized (mLock) { + mContrastChangeListeners.remove(listener); + } + } } diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java index 5d46de335781..7c32627dd1b1 100644 --- a/services/core/java/com/android/server/UiModeManagerService.java +++ b/services/core/java/com/android/server/UiModeManagerService.java @@ -16,6 +16,7 @@ package com.android.server; +import static android.app.UiModeManager.ContrastUtils.CONTRAST_DEFAULT_VALUE; import static android.app.UiModeManager.DEFAULT_PRIORITY; import static android.app.UiModeManager.MODE_NIGHT_AUTO; import static android.app.UiModeManager.MODE_NIGHT_CUSTOM; @@ -27,8 +28,11 @@ import static android.app.UiModeManager.MODE_NIGHT_YES; import static android.app.UiModeManager.PROJECTION_TYPE_AUTOMOTIVE; import static android.app.UiModeManager.PROJECTION_TYPE_NONE; import static android.os.UserHandle.USER_SYSTEM; +import static android.provider.Settings.Secure.CONTRAST_LEVEL; import static android.util.TimeUtils.isTimeBetween; +import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; + import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -38,6 +42,7 @@ import android.app.ActivityTaskManager; import android.app.AlarmManager; import android.app.IOnProjectionStateChangedListener; import android.app.IUiModeManager; +import android.app.IUiModeManagerCallback; import android.app.KeyguardManager; import android.app.Notification; import android.app.NotificationManager; @@ -71,6 +76,7 @@ import android.os.ShellCallback; import android.os.ShellCommand; import android.os.SystemProperties; import android.os.UserHandle; +import android.provider.Settings; import android.provider.Settings.Secure; import android.service.dreams.Sandman; import android.service.vr.IVrManager; @@ -192,6 +198,10 @@ final class UiModeManagerService extends SystemService { private final LocalService mLocalService = new LocalService(); private PowerManagerInternal mLocalPowerManager; + @GuardedBy("mLock") + private final RemoteCallbackList mUiModeManagerCallbacks = + new RemoteCallbackList(); + @GuardedBy("mLock") @Nullable private SparseArray> mProjectionHolders; @@ -199,6 +209,9 @@ final class UiModeManagerService extends SystemService { @Nullable private SparseArray> mProjectionListeners; + @GuardedBy("mLock") + private final SparseArray mContrasts = new SparseArray<>(); + public UiModeManagerService(Context context) { this(context, /* setupWizardComplete= */ false, /* tm= */ null, new Injector()); } @@ -352,6 +365,19 @@ final class UiModeManagerService extends SystemService { } }; + private final ContentObserver mContrastObserver = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange, Uri uri) { + synchronized (mLock) { + if (updateContrastLocked()) { + float contrast = getContrastLocked(); + mUiModeManagerCallbacks.broadcast(ignoreRemoteException(callback -> + callback.notifyContrastChanged(contrast))); + } + } + } + }; + private void updateSystemProperties() { int mode = Secure.getIntForUser(getContext().getContentResolver(), Secure.UI_NIGHT_MODE, mNightMode, 0); @@ -407,6 +433,9 @@ final class UiModeManagerService extends SystemService { context.getContentResolver() .registerContentObserver(Secure.getUriFor(Secure.UI_NIGHT_MODE), false, mDarkThemeObserver, 0); + context.getContentResolver().registerContentObserver( + Secure.getUriFor(Secure.CONTRAST_LEVEL), false, + mContrastObserver, UserHandle.USER_ALL); context.registerReceiver(mDockModeReceiver, new IntentFilter(Intent.ACTION_DOCK_EVENT)); IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED); @@ -633,6 +662,13 @@ final class UiModeManagerService extends SystemService { } private final IUiModeManager.Stub mService = new IUiModeManager.Stub() { + @Override + public void addCallback(IUiModeManagerCallback callback) { + synchronized (mLock) { + mUiModeManagerCallbacks.register(callback); + } + } + @Override public void enableCarMode(@UiModeManager.EnableCarMode int flags, @IntRange(from = 0) int priority, String callingPackage) { @@ -1132,6 +1168,13 @@ final class UiModeManagerService extends SystemService { } } } + + @Override + public float getContrast() { + synchronized (mLock) { + return getContrastLocked(); + } + } }; private void enforceProjectionTypePermissions(@UiModeManager.ProjectionType int p) { @@ -1214,6 +1257,30 @@ final class UiModeManagerService extends SystemService { } } + /** + * Return the contrast for the current user. If not cached, fetch it from the settings. + */ + @GuardedBy("mLock") + private float getContrastLocked() { + if (!mContrasts.contains(mCurrentUser)) updateContrastLocked(); + return mContrasts.get(mCurrentUser); + } + + /** + * Read the contrast setting for the current user and update {@link #mContrasts} + * if the contrast changed. Returns true if {@link #mContrasts} was updated. + */ + @GuardedBy("mLock") + private boolean updateContrastLocked() { + float contrast = Settings.Secure.getFloatForUser(getContext().getContentResolver(), + CONTRAST_LEVEL, CONTRAST_DEFAULT_VALUE, mCurrentUser); + if (Math.abs(mContrasts.get(mCurrentUser, Float.MAX_VALUE) - contrast) >= 1e-10) { + mContrasts.put(mCurrentUser, contrast); + return true; + } + return false; + } + private static class ProjectionHolder implements IBinder.DeathRecipient { private final String mPackageName; private final @UiModeManager.ProjectionType int mProjectionType; -- cgit v1.2.3-59-g8ed1b From 55c217124a13d45af89d82c8a39a593f852bafb6 Mon Sep 17 00:00:00 2001 From: Aurélien Pomini Date: Fri, 24 Feb 2023 15:39:17 +0000 Subject: Move contrast API: adapt ThemeOverlayController Part of the move of the API from AccessibilityManager to UiModeManager. Bug: 259091608 Test: atest SystemUITests Change-Id: Ic075ff37d7723af507c0ca6b24f2586e0cfd10a7 --- .../systemui/theme/ThemeOverlayController.java | 22 +++++++++++----------- .../systemui/theme/ThemeOverlayControllerTest.java | 12 ++++++------ 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java index 2ad3558fdb16..faae8df44ed9 100644 --- a/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java +++ b/packages/SystemUI/src/com/android/systemui/theme/ThemeOverlayController.java @@ -29,6 +29,7 @@ import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_INDEX import static com.android.systemui.theme.ThemeOverlayApplier.OVERLAY_COLOR_SOURCE; import static com.android.systemui.theme.ThemeOverlayApplier.TIMESTAMP_FIELD; +import android.app.UiModeManager; import android.app.WallpaperColors; import android.app.WallpaperManager; import android.app.WallpaperManager.OnColorsChangedListener; @@ -54,7 +55,6 @@ import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.util.SparseIntArray; -import android.view.accessibility.AccessibilityManager; import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; @@ -140,8 +140,8 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { private boolean mNeedsOverlayCreation; // Dominant color extracted from wallpaper, NOT the color used on the overlay protected int mMainWallpaperColor = Color.TRANSPARENT; - // UI contrast as reported by AccessibilityManager - private float mUiContrast = 0; + // UI contrast as reported by UiModeManager + private float mContrast = 0; // Theme variant: Vibrant, Tonal, Expressive, etc @VisibleForTesting protected Style mThemeStyle = Style.TONAL_SPOT; @@ -158,7 +158,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { private final SparseArray mDeferredWallpaperColors = new SparseArray<>(); private final SparseIntArray mDeferredWallpaperColorsFlags = new SparseIntArray(); private final WakefulnessLifecycle mWakefulnessLifecycle; - private final AccessibilityManager mAccessibilityManager; + private final UiModeManager mUiModeManager; private DynamicScheme mDynamicSchemeDark; private DynamicScheme mDynamicSchemeLight; @@ -392,7 +392,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { FeatureFlags featureFlags, @Main Resources resources, WakefulnessLifecycle wakefulnessLifecycle, - AccessibilityManager accessibilityManager) { + UiModeManager uiModeManager) { mContext = context; mIsMonochromaticEnabled = featureFlags.isEnabled(Flags.MONOCHROMATIC_THEME); mIsMonetEnabled = featureFlags.isEnabled(Flags.MONET); @@ -408,7 +408,7 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { mUserTracker = userTracker; mResources = resources; mWakefulnessLifecycle = wakefulnessLifecycle; - mAccessibilityManager = accessibilityManager; + mUiModeManager = uiModeManager; dumpManager.registerDumpable(TAG, this); } @@ -445,9 +445,9 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { } }, UserHandle.USER_ALL); - mUiContrast = mAccessibilityManager.getUiContrast(); - mAccessibilityManager.addUiContrastChangeListener(mMainExecutor, uiContrast -> { - mUiContrast = uiContrast; + mContrast = mUiModeManager.getContrast(); + mUiModeManager.addContrastChangeListener(mMainExecutor, contrast -> { + mContrast = contrast; // Force reload so that we update even when the main color has not changed reevaluateSystemTheme(true /* forceReload */); }); @@ -586,9 +586,9 @@ public class ThemeOverlayController implements CoreStartable, Dumpable { mSecondaryOverlay = createAccentOverlay(); mDynamicSchemeDark = dynamicSchemeFromStyle( - mThemeStyle, color, true /* isDark */, mUiContrast); + mThemeStyle, color, true /* isDark */, mContrast); mDynamicSchemeLight = dynamicSchemeFromStyle( - mThemeStyle, color, false /* isDark */, mUiContrast); + mThemeStyle, color, false /* isDark */, mContrast); mDynamicOverlay = createDynamicOverlay(); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java index f9b5767bf53c..17b5e0546eca 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/theme/ThemeOverlayControllerTest.java @@ -33,6 +33,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import android.app.UiModeManager; import android.app.WallpaperColors; import android.app.WallpaperManager; import android.content.BroadcastReceiver; @@ -47,7 +48,6 @@ import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.testing.AndroidTestingRunner; -import android.view.accessibility.AccessibilityManager; import androidx.annotation.VisibleForTesting; import androidx.test.filters.SmallTest; @@ -116,7 +116,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { @Mock private WakefulnessLifecycle mWakefulnessLifecycle; @Mock - private AccessibilityManager mAccessibilityManager; + private UiModeManager mUiModeManager; @Captor private ArgumentCaptor mBroadcastReceiver; @Captor @@ -135,7 +135,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { MockitoAnnotations.initMocks(this); when(mFeatureFlags.isEnabled(Flags.MONET)).thenReturn(true); when(mWakefulnessLifecycle.getWakefulness()).thenReturn(WAKEFULNESS_AWAKE); - when(mAccessibilityManager.getUiContrast()).thenReturn(0.5f); + when(mUiModeManager.getContrast()).thenReturn(0.5f); when(mDeviceProvisionedController.isCurrentUserSetup()).thenReturn(true); when(mResources.getColor(eq(android.R.color.system_accent1_500), any())) .thenReturn(Color.RED); @@ -151,7 +151,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mBroadcastDispatcher, mBgHandler, mMainExecutor, mBgExecutor, mThemeOverlayApplier, mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController, mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle, - mAccessibilityManager) { + mUiModeManager) { @VisibleForTesting protected boolean isNightMode() { return false; @@ -733,7 +733,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier, mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController, mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle, - mAccessibilityManager) { + mUiModeManager) { @VisibleForTesting protected boolean isNightMode() { return false; @@ -773,7 +773,7 @@ public class ThemeOverlayControllerTest extends SysuiTestCase { mBroadcastDispatcher, mBgHandler, executor, executor, mThemeOverlayApplier, mSecureSettings, mWallpaperManager, mUserManager, mDeviceProvisionedController, mUserTracker, mDumpManager, mFeatureFlags, mResources, mWakefulnessLifecycle, - mAccessibilityManager) { + mUiModeManager) { @VisibleForTesting protected boolean isNightMode() { return false; -- cgit v1.2.3-59-g8ed1b