Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 91f654bd authored by Aurélien Pomini's avatar Aurélien Pomini Committed by Android (Google) Code Review
Browse files

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"
parents 6c81f3c1 55c21712
Loading
Loading
Loading
Loading
+7 −7
Original line number Diff line number Diff line
@@ -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);
+11 −0
Original line number Diff line number Diff line
@@ -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();
}
+27 −0
Original line number Diff line number Diff line
/*
 * 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);
}
+170 −1
Original line number Diff line number Diff line
@@ -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> &nbsp; 0 corresponds to the default contrast </li>
     *     <li>       -1 corresponds to the minimum contrast </li>
     *     <li> &nbsp; 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);
        }
    }
}
+2 −117
Original line number Diff line number Diff line
@@ -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<HighTextContrastChangeListener, Handler>
            mHighTextContrastStateChangeListeners = new ArrayMap<>();

    private final ArrayMap<UiContrastChangeListener, Executor>
            mUiContrastChangeListeners = new ArrayMap<>();

    private final ArrayMap<AccessibilityServicesStateChangeListener, Executor>
            mServicesStateChangeListeners = new ArrayMap<>();

@@ -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.
     * <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>
     *
@@ -739,24 +692,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}.
     *
@@ -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<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.
     */
@@ -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;
        }
Loading