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

Commit 410d4e33 authored by Alan Viverette's avatar Alan Viverette
Browse files

Add accessibility display adjustments

BUG: 9057596
Change-Id: I5e7cc05159387cb00701e162d015e2f7ca6cbf4c
parent 97f5d816
Loading
Loading
Loading
Loading
+91 −0
Original line number Diff line number Diff line
@@ -3751,6 +3751,97 @@ public final class Settings {
        public static final String ACCESSIBILITY_CAPTIONING_FONT_SCALE =
                "accessibility_captioning_font_scale";

        /**
         * Setting that specifies whether the quick setting tile for display
         * color inversion is enabled.
         *
         * @hide
         */
        public static final String ACCESSIBILITY_DISPLAY_INVERSION_QUICK_SETTING_ENABLED =
                "accessibility_display_inversion_quick_setting_enabled";

        /**
         * Setting that specifies whether display color inversion is enabled.
         *
         * @hide
         */
        public static final String ACCESSIBILITY_DISPLAY_INVERSION_ENABLED =
                "accessibility_display_inversion_enabled";

        /**
         * Integer property that specifies the type of color inversion to
         * perform. Valid values are defined in AccessibilityManager.
         *
         * @hide
         */
        public static final String ACCESSIBILITY_DISPLAY_INVERSION =
                "accessibility_display_inversion";

        /**
         * Setting that specifies whether the quick setting tile for display
         * color space adjustment is enabled.
         *
         * @hide
         */
        public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_QUICK_SETTING_ENABLED =
                "accessibility_display_daltonizer_quick_setting_enabled";

        /**
         * Setting that specifies whether display color space adjustment is
         * enabled.
         *
         * @hide
         */
        public static final String ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED =
                "accessibility_display_daltonizer_enabled";

        /**
         * Integer property that specifies the type of color space adjustment to
         * perform. Valid values are defined in AccessibilityManager.
         *
         * @hide
         */
        public static final String ACCESSIBILITY_DISPLAY_DALTONIZER =
                "accessibility_display_daltonizer";

        /**
         * Setting that specifies whether the quick setting tile for display
         * contrast enhancement is enabled.
         *
         * @hide
         */
        public static final String ACCESSIBILITY_DISPLAY_CONTRAST_QUICK_SETTING_ENABLED =
                "accessibility_display_contrast_quick_setting_enabled";

        /**
         * Setting that specifies whether display contrast enhancement is
         * enabled.
         *
         * @hide
         */
        public static final String ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED =
                "accessibility_display_contrast_enabled";

        /**
         * Floating point property that specifies display contrast adjustment.
         * Valid range is [0, ...] where 0 is gray, 1 is normal, and higher
         * values indicate enhanced contrast.
         *
         * @hide
         */
        public static final String ACCESSIBILITY_DISPLAY_CONTRAST =
                "accessibility_display_contrast";

        /**
         * Floating point property that specifies display brightness adjustment.
         * Valid range is [-1, 1] where -1 is black, 0 is default, and 1 is
         * white.
         *
         * @hide
         */
        public static final String ACCESSIBILITY_DISPLAY_BRIGHTNESS =
                "accessibility_display_brightness";

        /**
         * The timout for considering a press to be a long press in milliseconds.
         * @hide
+119 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2013 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.util;

import java.util.Arrays;

/**
 * A class that contains utility methods related to matrices.
 *
 * @hide
 */
public class MatrixUtils {
    private MatrixUtils() {
        // This class is non-instantiable.
    }

    /**
     * Sets a matrix to the identity matrix.
     *
     * @param out output matrix of size n*m
     * @param n number of rows in the output matrix
     * @param m number of columns in the output matrix
     */
    public static void setIdentityM(float[] out, int n, int m) {
        final int size = n * m;
        if (out.length != size) {
            throw new IllegalArgumentException("Invalid dimensions for output matrix");
        }

        Arrays.fill(out, 0, size, 0);
        for (int i = Math.min(m,n) - 1; i >= 0; i--) {
            out[i * (m + 1)] = 1;
        }
    }

    /**
     * Add two matrices. May be used in-place by specifying the same value for
     * the out matrix and one of the input matrices.
     *
     * @param ab output matrix of size n*m
     * @param a left-hand matrix of size n*m
     * @param n number of rows in the left-hand matrix
     * @param m number of columns in the left-hand matrix
     * @param b right-hand matrix of size n*m
     */
    public static void addMM(float[] ab, float[] a, int n, int m, float[] b) {
        final int size = n * m;
        if (a.length != size) {
            throw new IllegalArgumentException("Invalid dimensions for matrix a");
        } else if (b.length != size) {
            throw new IllegalArgumentException("Invalid dimensions for matrix b");
        } else if (ab.length != size) {
            throw new IllegalArgumentException("Invalid dimensions for matrix ab");
        }

        for (int i = 0; i < size; i++) {
            ab[i] = a[i] + b[i];
        }
    }

    /**
     * Multiply two matrices.
     *
     * @param ab output matrix of size n*p
     * @param a left-hand matrix of size n*m
     * @param n number of rows in the left-hand matrix
     * @param m number of columns in the left-hand matrix
     * @param b right-hand matrix of size m*p
     * @param p number of columns in the right-hand matrix
     */
    public static void multiplyMM(float[] ab, float[] a, int n, int m, float[] b, int p) {
        if (a.length != n * m) {
            throw new IllegalArgumentException("Invalid dimensions for matrix a");
        } else if (b.length != m * p) {
            throw new IllegalArgumentException("Invalid dimensions for matrix b");
        } else if (ab.length != n * p) {
            throw new IllegalArgumentException("Invalid dimensions for matrix ab");
        }

        for (int i = 0; i < n; i++) {
            for (int j = 0; j < p; j++) {
                float sum = 0;
                for (int k = 0; k < m; k++) {
                    sum += a[i * m + k] * b[k * p + j];
                }
                ab[i * p + j] = sum;
            }
        }
    }

    /**
     * Multiply a matrix by a scalar value. May be used in-place by specifying
     * the same value for the input and output matrices.
     *
     * @param out output matrix
     * @param in input matrix
     * @param scalar scalar value
     */
    public static void multiplyMS(float[] out, float[] in, float scalar) {
        final int n = out.length;
        for (int i = 0; i < n; i++) {
            out[i] = in[i] * scalar;
        }
    }
}
+36 −0
Original line number Diff line number Diff line
@@ -75,6 +75,42 @@ public final class AccessibilityManager {
    /** @hide */
    public static final int STATE_FLAG_TOUCH_EXPLORATION_ENABLED = 0x00000002;

    /** @hide */
    public static final int INVERSION_DISABLED = -1;

    /** @hide */
    public static final int INVERSION_STANDARD = 0;

    /** @hide */
    public static final int INVERSION_HUE_ONLY = 1;

    /** @hide */
    public static final int INVERSION_VALUE_ONLY = 2;

    /** @hide */
    public static final int DALTONIZER_DISABLED = -1;

    /** @hide */
    public static final int DALTONIZER_SIMULATE_MONOCHROMACY = 0;

    /** @hide */
    public static final int DALTONIZER_SIMULATE_PROTANOMALY = 1;

    /** @hide */
    public static final int DALTONIZER_SIMULATE_DEUTERANOMALY = 2;

    /** @hide */
    public static final int DALTONIZER_SIMULATE_TRITANOMALY = 3;

    /** @hide */
    public static final int DALTONIZER_CORRECT_PROTANOMALY = 11;

    /** @hide */
    public static final int DALTONIZER_CORRECT_DEUTERANOMALY = 12;

    /** @hide */
    public static final int DALTONIZER_CORRECT_TRITANOMALY = 13;

    static final Object sInstanceSync = new Object();

    private static AccessibilityManager sInstance;
+285 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.Parcel;
import android.os.Process;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
@@ -61,6 +62,7 @@ import android.os.UserManager;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
import android.util.MatrixUtils;
import android.util.Pools.Pool;
import android.util.Pools.SimplePool;
import android.util.Slog;
@@ -139,6 +141,56 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {

    private static final int MAX_POOL_SIZE = 10;

    /** Matrix used for converting color to grayscale. */
    private static final float[] GRAYSCALE_MATRIX = new float[] {
        .2126f, .2126f, .2126f,
        .7152f, .7152f, .7152f,
        .0722f, .0722f, .0722f
    };

    /** Matrix used for standard display inversion. */
    private static final float[] INVERSION_MATRIX_STANDARD = new float[] {
        -1,  0,  0,
         0, -1,  0,
         0,  0, -1
    };

    /** Offset used for standard display inversion. */
    private static final float INVERSION_OFFSET_STANDARD = 1;

    /** Matrix and offset used for hue-only display inversion. */
    private static final float[] INVERSION_MATRIX_HUE_ONLY = new float[] {
          0, .5f, .5f,
        .5f,   0, .5f,
        .5f, .5f,   0
    };

    /** Offset used for hue-only display inversion. */
    private static final float INVERSION_OFFSET_HUE_ONLY = 0;

    /** Matrix and offset used for value-only display inversion. */
    private static final float[] INVERSION_MATRIX_VALUE_ONLY = new float[] {
           0, -.5f, -.5f,
        -.5f,    0, -.5f,
        -.5f, -.5f,    0
    };

    /** Offset used for value-only display inversion. */
    private static final float INVERSION_OFFSET_VALUE_ONLY = 1;

    /** Default contrast for display contrast enhancement. */
    private static final float DEFAULT_DISPLAY_CONTRAST = 2;

    /** Default brightness for display contrast enhancement. */
    private static final float DEFAULT_DISPLAY_BRIGHTNESS = 0;

    /** Default inversion mode for display color inversion. */
    private static final int DEFAULT_DISPLAY_INVERSION = AccessibilityManager.INVERSION_STANDARD;

    /** Default inversion mode for display color correction. */
    private static final int DEFAULT_DISPLAY_DALTONIZER =
            AccessibilityManager.DALTONIZER_CORRECT_DEUTERANOMALY;

    private static int sIdCounter = 0;

    private static int sNextWindowId;
@@ -1297,6 +1349,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        updateFilterKeyEventsLocked(userState);
        updateTouchExplorationLocked(userState);
        updateEnhancedWebAccessibilityLocked(userState);
        updateDisplayColorAdjustmentSettingsLocked(userState);
        scheduleUpdateInputFilter(userState);
        scheduleUpdateClientsIfNeededLocked(userState);
    }
@@ -1355,6 +1408,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        somthingChanged |= readTouchExplorationEnabledSettingLocked(userState);
        somthingChanged |= readEnhancedWebAccessibilityEnabledChangedLocked(userState);
        somthingChanged |= readDisplayMagnificationEnabledSettingLocked(userState);
        somthingChanged |= readDisplayColorAdjustmentSettingsLocked(userState);
        return somthingChanged;
    }

@@ -1403,6 +1457,136 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
         return false;
    }

    private boolean readDisplayColorAdjustmentSettingsLocked(UserState userState) {
        final ContentResolver cr = mContext.getContentResolver();
        final int userId = userState.mUserId;

        boolean hasColorTransform = Settings.Secure.getIntForUser(
                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) == 1;

        if (!hasColorTransform) {
            hasColorTransform |= Settings.Secure.getIntForUser(
                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, userId) == 1;
        }

        if (!hasColorTransform) {
            hasColorTransform |= Settings.Secure.getIntForUser(
                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) == 1;
        }

        if (userState.mHasDisplayColorAdjustment != hasColorTransform) {
            userState.mHasDisplayColorAdjustment = hasColorTransform;
            return true;
        }

        // If adjustment is enabled, always assume there was a transform change.
        return hasColorTransform;
    }

    private void updateDisplayColorAdjustmentSettingsLocked(UserState userState) {
        final ContentResolver cr = mContext.getContentResolver();
        final int userId = userState.mUserId;
        final float[] offsetVector = new float[3];
        float[] colorOffset = new float[3];
        float[] outputOffset = new float[3];
        float[] colorMatrix = new float[9];
        float[] outputMatrix = new float[9];
        boolean hasColorTransform = false;

        MatrixUtils.setIdentityM(colorMatrix, 3, 3);

        final boolean inversionEnabled = Settings.Secure.getIntForUser(
                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0, userId) == 1;
        if (inversionEnabled) {
            final int inversionMode = Settings.Secure.getIntForUser(cr,
                    Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION, DEFAULT_DISPLAY_INVERSION,
                    userId);
            final float[] inversionMatrix;
            final float inversionOffset;
            switch (inversionMode) {
                case AccessibilityManager.INVERSION_HUE_ONLY:
                    inversionMatrix = INVERSION_MATRIX_HUE_ONLY;
                    inversionOffset = INVERSION_OFFSET_HUE_ONLY;
                    break;
                case AccessibilityManager.INVERSION_VALUE_ONLY:
                    inversionMatrix = INVERSION_MATRIX_VALUE_ONLY;
                    inversionOffset = INVERSION_OFFSET_VALUE_ONLY;
                    break;
                default:
                    inversionMatrix = INVERSION_MATRIX_STANDARD;
                    inversionOffset = INVERSION_OFFSET_STANDARD;
            }

            Arrays.fill(offsetVector, inversionOffset);

            MatrixUtils.multiplyMM(outputMatrix, colorMatrix, 3, 3, inversionMatrix, 3);
            MatrixUtils.multiplyMM(outputOffset, colorOffset, 1, 3, inversionMatrix, 3);
            MatrixUtils.addMM(colorOffset, outputOffset, 1, 3, offsetVector);

            final float[] temp = colorMatrix;
            colorMatrix = outputMatrix;
            outputMatrix = temp;

            hasColorTransform = true;
        }

        final boolean contrastEnabled = Settings.Secure.getIntForUser(
                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED, 0, userId) == 1;
        if (contrastEnabled) {
            final float contrast = Settings.Secure.getFloatForUser(cr,
                    Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST, DEFAULT_DISPLAY_CONTRAST,
                    userId);
            final float brightness = Settings.Secure.getFloatForUser(cr,
                    Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS, DEFAULT_DISPLAY_BRIGHTNESS,
                    userId);
            final float offset = brightness * contrast - 0.5f * contrast + 0.5f;
            Arrays.fill(offsetVector, offset);

            MatrixUtils.multiplyMS(outputMatrix, colorMatrix, contrast);
            MatrixUtils.multiplyMS(outputOffset, colorOffset, contrast);
            MatrixUtils.addMM(colorOffset, outputOffset, 1, 3, offsetVector);

            final float[] temp = colorMatrix;
            colorMatrix = outputMatrix;
            outputMatrix = temp;

            hasColorTransform = true;
        }

        final boolean daltonizerEnabled = Settings.Secure.getIntForUser(
                cr, Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED, 0, userId) != 0;
        if (daltonizerEnabled) {
            final int daltonizerMode = Settings.Secure.getIntForUser(cr,
                    Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER, DEFAULT_DISPLAY_DALTONIZER,
                    userId);
            // Monochromacy isn't supported by the native Daltonizer.
            if (daltonizerMode == AccessibilityManager.DALTONIZER_SIMULATE_MONOCHROMACY) {
                MatrixUtils.multiplyMM(outputMatrix, colorMatrix, 3, 3, GRAYSCALE_MATRIX, 3);
                MatrixUtils.multiplyMM(outputOffset, colorOffset, 1, 3, GRAYSCALE_MATRIX, 3);

                final float[] temp = colorMatrix;
                colorMatrix = outputMatrix;
                outputMatrix = temp;

                final float[] tempVec = colorOffset;
                colorOffset = outputOffset;
                outputOffset = temp;

                nativeSetDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
            } else {
                nativeSetDaltonizerMode(daltonizerMode);
            }
        } else {
            nativeSetDaltonizerMode(AccessibilityManager.DALTONIZER_DISABLED);
        }

        if (hasColorTransform) {
            nativeSetColorTransform(colorMatrix, colorOffset);
        } else {
            nativeSetColorTransform(null, null);
        }
    }

    private void updateTouchExplorationLocked(UserState userState) {
        boolean enabled = false;
        final int serviceCount = userState.mBoundServices.size();
@@ -1524,6 +1708,59 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        }
    }

    /**
     * Sets the surface flinger's Daltonization mode. This adjusts the color
     * space to correct for or simulate various types of color blindness.
     *
     * @param mode new Daltonization mode
     */
    private static void nativeSetDaltonizerMode(int mode) {
        try {
            final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
            if (flinger != null) {
                final Parcel data = Parcel.obtain();
                data.writeInterfaceToken("android.ui.ISurfaceComposer");
                data.writeInt(mode);
                flinger.transact(1014, data, null, 0);
                data.recycle();
            }
        } catch (RemoteException ex) {
            Slog.e(LOG_TAG, "Failed to set Daltonizer mode", ex);
        }
    }

    /**
     * Sets the surface flinger's color transformation matrix and offset. If
     * either value is null, color transformations are disabled.
     *
     * @param matrix new color transformation matrix, or null to disable
     * @param offset new color transformation offset, or null to disable
     */
    private static void nativeSetColorTransform(float[] matrix, float[] offset) {
        try {
            final IBinder flinger = ServiceManager.getService("SurfaceFlinger");
            if (flinger != null) {
                final Parcel data = Parcel.obtain();
                data.writeInterfaceToken("android.ui.ISurfaceComposer");
                if (matrix != null && offset != null) {
                    data.writeInt(1);
                    for (int i = 0; i < 9; i++) {
                        data.writeFloat(matrix[i]);
                    }
                    for (int i = 0; i < 3; i++) {
                        data.writeFloat(offset[i]);
                    }
                } else {
                    data.writeInt(0);
                }
                flinger.transact(1015, data, null, 0);
                data.recycle();
            }
        } catch (RemoteException ex) {
            Slog.e(LOG_TAG, "Failed to set color transform", ex);
        }
    }

    private class AccessibilityConnectionWrapper implements DeathRecipient {
        private final int mWindowId;
        private final int mUserId;
@@ -2947,6 +3184,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        public boolean mIsEnhancedWebAccessibilityEnabled;
        public boolean mIsDisplayMagnificationEnabled;
        public boolean mIsFilterKeyEventsEnabled;
        public boolean mHasDisplayColorAdjustment;

        private Service mUiAutomationService;
        private IAccessibilityServiceClient mUiAutomationServiceClient;
@@ -3038,6 +3276,23 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
        private final Uri mEnhancedWebAccessibilityUri = Settings.Secure
                .getUriFor(Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION);

        private final Uri mDisplayContrastEnabledUri = Settings.Secure.getUriFor(
                Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST_ENABLED);
        private final Uri mDisplayContrastUri = Settings.Secure.getUriFor(
                Settings.Secure.ACCESSIBILITY_DISPLAY_CONTRAST);
        private final Uri mDisplayBrightnessUri = Settings.Secure.getUriFor(
                Settings.Secure.ACCESSIBILITY_DISPLAY_BRIGHTNESS);

        private final Uri mDisplayInversionEnabledUri = Settings.Secure.getUriFor(
                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED);
        private final Uri mDisplayInversionUri = Settings.Secure.getUriFor(
                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION);

        private final Uri mDisplayDaltonizerEnabledUri = Settings.Secure.getUriFor(
                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER_ENABLED);
        private final Uri mDisplayDaltonizerUri = Settings.Secure.getUriFor(
                Settings.Secure.ACCESSIBILITY_DISPLAY_DALTONIZER);

        public AccessibilityContentObserver(Handler handler) {
            super(handler);
        }
@@ -3056,6 +3311,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                    false, this, UserHandle.USER_ALL);
            contentResolver.registerContentObserver(mEnhancedWebAccessibilityUri,
                    false, this, UserHandle.USER_ALL);
            contentResolver.registerContentObserver(
                    mDisplayContrastEnabledUri, false, this, UserHandle.USER_ALL);
            contentResolver.registerContentObserver(
                    mDisplayContrastUri, false, this, UserHandle.USER_ALL);
            contentResolver.registerContentObserver(
                    mDisplayBrightnessUri, false, this, UserHandle.USER_ALL);
            contentResolver.registerContentObserver(
                    mDisplayInversionEnabledUri, false, this, UserHandle.USER_ALL);
            contentResolver.registerContentObserver(
                    mDisplayInversionUri, false, this, UserHandle.USER_ALL);
            contentResolver.registerContentObserver(
                    mDisplayDaltonizerEnabledUri, false, this, UserHandle.USER_ALL);
            contentResolver.registerContentObserver(
                    mDisplayDaltonizerUri, false, this, UserHandle.USER_ALL);
        }

        @Override
@@ -3120,6 +3389,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub {
                        }
                    }
                }
            } else if (mDisplayContrastEnabledUri.equals(uri)
                    || mDisplayInversionEnabledUri.equals(uri)
                    || mDisplayDaltonizerEnabledUri.equals(uri)
                    || mDisplayContrastUri.equals(uri)
                    || mDisplayBrightnessUri.equals(uri)
                    || mDisplayInversionUri.equals(uri)
                    || mDisplayDaltonizerUri.equals(uri)) {
                synchronized (mLock) {
                    // We will update when the automation service dies.
                    UserState userState = getCurrentUserStateLocked();
                    if (userState.mUiAutomationService == null) {
                        if (readDisplayColorAdjustmentSettingsLocked(userState)) {
                            updateDisplayColorAdjustmentSettingsLocked(userState);
                        }
                    }
                }
            }
        }
    }