diff options
| author | 2023-03-08 11:57:11 +0000 | |
|---|---|---|
| committer | 2023-03-08 11:57:11 +0000 | |
| commit | 91f654bdc9c369ff383be04c74eb4ea965d9994b (patch) | |
| tree | 85a990a693e1edd690561d62bff7c0afaac2b0b6 | |
| parent | 6c81f3c188cbedc92417ea93bfb132075ac7a940 (diff) | |
| parent | 55c217124a13d45af89d82c8a39a593f852bafb6 (diff) | |
Merge changes from topic "move_contrast_api" into udc-dev
* changes:
Move contrast API: adapt ThemeOverlayController
Add the contrast API to UiModeManager
Revert "Add API to get/listen to the color contrast setting"
12 files changed, 301 insertions, 219 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 37480520529b..6f206eee3786 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(); @@ -54161,14 +54168,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<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(); @@ -54180,7 +54185,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 @@ -54203,10 +54207,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/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,6 +17,7 @@ package android.app; import android.app.IOnProjectionStateChangedListener; +import android.app.IUiModeManagerCallback; /** * Interface used to control special UI modes. @@ -24,6 +25,11 @@ import android.app.IOnProjectionStateChangedListener; */ 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<String> 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. + * <p> + * 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. + * </p> + * <p> + * {@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. + * </p> + * <p> + * Currently, a user chan chose from three contrast levels: + * <ul> + * <li>{@link #CONTRAST_LEVEL_STANDARD}, corresponding to the default contrast value 0f</li> + * <li>{@link #CONTRAST_LEVEL_MEDIUM}, corresponding to the contrast value 0.5f</li> + * <li>{@link #CONTRAST_LEVEL_HIGH}, corresponding to the maximum contrast value 1f</li> + * </ul> + * </p> + * + * @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<Integer> 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<ContrastChangeListener, Executor> + mContrastChangeListeners = new ArrayMap<>(); + private float mContrast; + + private final IUiModeManagerCallback.Stub mCallback = new IUiModeManagerCallback.Stub() { + @Override + public void notifyContrastChanged(float contrast) { + final ArrayMap<ContrastChangeListener, Executor> 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. + * <p> + * <strong>Note:</strong> You need to query this only if your application is + * doing its own rendering and does not rely on the material rendering pipeline. + * </p> + * @return The color contrast, float in [-1, 1] where + * <ul> + * <li> 0 corresponds to the default contrast </li> + * <li> -1 corresponds to the minimum contrast </li> + * <li> 1 corresponds to the maximum contrast </li> + * </ul> + * + * + * + */ + @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/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; /** @@ -140,21 +138,6 @@ 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> @@ -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<HighTextContrastChangeListener, Handler> mHighTextContrastStateChangeListeners = new ArrayMap<>(); - private final ArrayMap<UiContrastChangeListener, Executor> - mUiContrastChangeListeners = new ArrayMap<>(); - private final ArrayMap<AccessibilityServicesStateChangeListener, Executor> 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); } /** @@ -412,21 +390,6 @@ 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 @@ -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. * <p> - * <strong>Note:</strong> You need to query this only if your application is + * <strong>Note:</strong> You need to query this only if you application is * doing its own rendering and does not rely on the platform rendering pipeline. * </p> * @@ -740,24 +693,6 @@ 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. @@ -1346,35 +1281,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. * The value could be read via {@link #isAudioDescriptionRequested}. @@ -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); @@ -2311,22 +2216,6 @@ 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() { @@ -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/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<WallpaperColors> 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<BroadcastReceiver> 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; 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; - } } 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; @@ -193,12 +199,19 @@ final class UiModeManagerService extends SystemService { private PowerManagerInternal mLocalPowerManager; @GuardedBy("mLock") + private final RemoteCallbackList<IUiModeManagerCallback> mUiModeManagerCallbacks = + new RemoteCallbackList<IUiModeManagerCallback>(); + + @GuardedBy("mLock") @Nullable private SparseArray<List<ProjectionHolder>> mProjectionHolders; @GuardedBy("mLock") @Nullable private SparseArray<RemoteCallbackList<IOnProjectionStateChangedListener>> mProjectionListeners; + @GuardedBy("mLock") + private final SparseArray<Float> 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); @@ -634,6 +663,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) { if (isUiModeLocked()) { @@ -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; |