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

Commit f92160da authored by PETER LIANG's avatar PETER LIANG Committed by Android (Google) Code Review
Browse files

Merge changes from topic "color_correction_b_148785841"

* changes:
  Fix the screen will move by itself when enabled Color correction.
  Refine and migrate the functions that related to update preference into the specific lifecycle.
parents f164cf85 896e1801
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -17,13 +17,13 @@

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/preview_viewport"
    android:layout_width="wrap_content"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <com.android.settings.accessibility.PaletteListView
        android:id="@+id/palette_listView"
    <LinearLayout
        android:id="@+id/palette_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:importantForAccessibility="noHideDescendants"/>

</FrameLayout>
+2 −2
Original line number Diff line number Diff line
@@ -1481,7 +1481,7 @@
    </string-array>

    <!-- Array of titles palette list for accessibility. -->
    <string-array name="setting_palette_colors" translatable="false" >
    <string-array name="setting_palette_data" translatable="false" >
        <item>@string/color_red</item>
        <item>@string/color_orange</item>
        <item>@string/color_yellow</item>
@@ -1492,7 +1492,7 @@
    </string-array>

    <!-- Values for palette list view preference. -->
    <array name="setting_palette_data" translatable="false" >
    <array name="setting_palette_colors" translatable="false" >
        <item>@color/palette_list_color_red</item>
        <item>@color/palette_list_color_orange</item>
        <item>@color/palette_list_color_yellow</item>
+121 −39
Original line number Diff line number Diff line
@@ -16,23 +16,58 @@

package com.android.settings.accessibility;

import static android.graphics.drawable.GradientDrawable.Orientation;

import static com.android.settings.accessibility.AccessibilityUtil.getScreenHeightPixels;
import static com.android.settings.accessibility.AccessibilityUtil.getScreenWidthPixels;

import static com.google.common.primitives.Ints.max;

import android.content.Context;
import android.graphics.Paint.FontMetrics;
import android.graphics.drawable.GradientDrawable;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.view.ViewGroup;
import android.widget.TextView;

import androidx.annotation.ColorInt;
import androidx.annotation.IntDef;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;

import com.android.settings.R;

import com.google.common.primitives.Floats;
import com.google.common.primitives.Ints;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/** Preference that easier preview by matching name to color. */
public class PaletteListPreference extends Preference {
public final class PaletteListPreference extends Preference {

    private ListView mListView;
    private ViewTreeObserver.OnPreDrawListener mPreDrawListener;
    private final List<Integer> mGradientColors = new ArrayList<>();
    private final List<Float> mGradientOffsets = new ArrayList<>();

    @IntDef({
            Position.START,
            Position.CENTER,
            Position.END,
    })
    @Retention(RetentionPolicy.SOURCE)
    @interface Position {
        int START = 0;
        int CENTER = 1;
        int END = 2;
    }

    /**
     * Constructs a new PaletteListPreference with the given context's theme and the supplied
@@ -61,47 +96,94 @@ public class PaletteListPreference extends Preference {
    public PaletteListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setLayoutResource(R.layout.daltonizer_preview);
        initPreDrawListener();
    }

    @Override
    public void onBindViewHolder(PreferenceViewHolder holder) {
        super.onBindViewHolder(holder);

        final View rootView = holder.itemView;
        mListView = rootView.findViewById(R.id.palette_listView);
        if (mPreDrawListener != null) {
            mListView.getViewTreeObserver().addOnPreDrawListener(mPreDrawListener);
        final ViewGroup paletteView = holder.itemView.findViewById(R.id.palette_view);
        initPaletteAttributes(getContext());
        initPaletteView(getContext(), paletteView);
    }

    private void initPaletteAttributes(Context context) {
        final int defaultColor = context.getColor(R.color.palette_list_gradient_background);
        mGradientColors.add(Position.START, defaultColor);
        mGradientColors.add(Position.CENTER, defaultColor);
        mGradientColors.add(Position.END, defaultColor);

        mGradientOffsets.add(Position.START, /* element= */ 0.0f);
        mGradientOffsets.add(Position.CENTER, /* element= */ 0.5f);
        mGradientOffsets.add(Position.END, /* element= */ 1.0f);
    }

    private void initPreDrawListener() {
        mPreDrawListener = new ViewTreeObserver.OnPreDrawListener() {
            @Override
            public boolean onPreDraw() {
                if (mListView == null) {
                    return false;
    private void initPaletteView(Context context, ViewGroup rootView) {
        if (rootView.getChildCount() > 0) {
            rootView.removeAllViews();
        }

        final List<Integer> paletteColors = getPaletteColors(context);
        final List<String> paletteData = getPaletteData(context);

        final float textPadding =
                context.getResources().getDimension(R.dimen.accessibility_layout_margin_start_end);
        final String maxLengthData =
                Collections.max(paletteData, Comparator.comparing(String::length));
        final int textWidth = getTextWidth(context, maxLengthData);
        final float textBound = (textWidth + textPadding) / getScreenWidthPixels(context);
        mGradientOffsets.set(Position.CENTER, textBound);

        final int screenHalfHeight = getScreenHeightPixels(context) / 2;
        final int paletteItemHeight =
                max(screenHalfHeight / paletteData.size(), getTextLineHeight(context));

        for (int i = 0; i < paletteData.size(); ++i) {
            final TextView textView = new TextView(context);
            textView.setText(paletteData.get(i));
            textView.setHeight(paletteItemHeight);
            textView.setPaddingRelative(Math.round(textPadding), 0, 0, 0);
            textView.setGravity(Gravity.CENTER_VERTICAL);
            textView.setBackground(createGradientDrawable(rootView, paletteColors.get(i)));

            rootView.addView(textView);
        }
    }

                final int listViewHeight = mListView.getMeasuredHeight();
                final int listViewWidth = mListView.getMeasuredWidth();
    private GradientDrawable createGradientDrawable(ViewGroup rootView, @ColorInt int color) {
        mGradientColors.set(Position.END, color);

                // Removes the callback after get result of measure view.
                final ViewTreeObserver viewTreeObserver = mListView.getViewTreeObserver();
                if (viewTreeObserver.isAlive()) {
                    viewTreeObserver.removeOnPreDrawListener(this);
        final GradientDrawable gradientDrawable = new GradientDrawable();
        final Orientation orientation =
                rootView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
                        ? Orientation.RIGHT_LEFT
                        : Orientation.LEFT_RIGHT;
        gradientDrawable.setOrientation(orientation);
        gradientDrawable.setColors(Ints.toArray(mGradientColors), Floats.toArray(mGradientOffsets));

        return gradientDrawable;
    }
                mPreDrawListener = null;

                // Resets layout parameters to display whole items from listView.
                final FrameLayout.LayoutParams layoutParams =
                        (FrameLayout.LayoutParams) mListView.getLayoutParams();
                layoutParams.height = listViewHeight * mListView.getAdapter().getCount();
                layoutParams.width = listViewWidth;
                mListView.setLayoutParams(layoutParams);
    private List<Integer> getPaletteColors(Context context) {
        final int[] paletteResources =
                context.getResources().getIntArray(R.array.setting_palette_colors);
        return Arrays.stream(paletteResources).boxed().collect(Collectors.toList());
    }

                return true;
    private List<String> getPaletteData(Context context) {
        final String[] paletteResources =
                context.getResources().getStringArray(R.array.setting_palette_data);
        return Arrays.asList(paletteResources);
    }
        };

    private int getTextWidth(Context context, String text) {
        final TextView tempView = new TextView(context);
        return Math.round(tempView.getPaint().measureText(text));
    }

    private int getTextLineHeight(Context context) {
        final TextView tempView = new TextView(context);
        final FontMetrics fontMetrics = tempView.getPaint().getFontMetrics();
        return Math.round(fontMetrics.bottom - fontMetrics.top);
    }
}
+0 −301
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.settings.accessibility;

import android.annotation.NonNull;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.GradientDrawable.Orientation;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

import androidx.annotation.VisibleForTesting;

import com.android.settings.R;

import com.google.common.collect.Iterables;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * Custom ListView {@link ListView} which displays palette to deploy the color code preview.
 *
 * <p>The preview shows gradient from color white to specific color code on each list view item, in
 * addition, text view adjusts the attribute of width for adapting the text length.
 *
 * <p>The text cannot fills the whole view for ensuring the gradient color preview can purely
 * display also the view background shows the color beside the text variable end point.
 */
public class PaletteListView extends ListView {
    private final Context mContext;
    private final DisplayAdapter mDisplayAdapter;
    private final LayoutInflater mLayoutInflater;
    private final String mDefaultGradientColorCodeString;
    private final int mDefaultGradientColor;
    private float mTextBound;
    private static final float LANDSCAPE_MAX_WIDTH_PERCENTAGE = 100f;

    public PaletteListView(Context context) {
        this(context, null);
    }

    public PaletteListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public PaletteListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        mDisplayAdapter = new DisplayAdapter();
        mLayoutInflater = LayoutInflater.from(context);
        mDefaultGradientColorCodeString =
                getResources().getString(R.color.palette_list_gradient_background);
        mDefaultGradientColor =
                getResources().getColor(R.color.palette_list_gradient_background, null);
        mTextBound = 0.0f;
        init();
    }

    private static int getScreenWidth(WindowManager windowManager) {
        final Display display = windowManager.getDefaultDisplay();
        final DisplayMetrics displayMetrics = new DisplayMetrics();
        display.getMetrics(displayMetrics);
        return displayMetrics.widthPixels;
    }

    private void init() {
        final TypedArray colorNameArray = getResources().obtainTypedArray(
                R.array.setting_palette_colors);
        final TypedArray colorCodeArray = getResources().obtainTypedArray(
                R.array.setting_palette_data);
        final int colorNameArrayLength = colorNameArray.length();
        final List<ColorAttributes> colorList = new ArrayList<>();
        computeTextWidthBounds(colorNameArray);

        for (int index = 0; index < colorNameArrayLength; index++) {
            colorList.add(
                    new ColorAttributes(
                            /* colorName= */ colorNameArray.getString(index),
                            /* colorCode= */ colorCodeArray.getColor(index, mDefaultGradientColor),
                            /* textBound= */ mTextBound,
                            /* gradientDrawable= */
                            new GradientDrawable(Orientation.LEFT_RIGHT, null)));
        }

        mDisplayAdapter.setColorList(colorList);
        setAdapter(mDisplayAdapter);
        setDividerHeight(/* height= */ 0);
    }

    /**
     * Sets string array that required the color name and color code for deploy the new color
     * preview.
     *
     * <p>The parameters not allow null define but two array length inconsistent are acceptable, in
     * addition, to prevent IndexOutOfBoundsException the algorithm will check array data, and base
     * on the array size to display data, or fills color code array if length less than other.
     *
     * @param colorNames a string array of color name
     * @param colorCodes a string array of color code
     * @return true if new array data apply successful
     */
    @VisibleForTesting
    boolean setPaletteListColors(@NonNull String[] colorNames, @NonNull String[] colorCodes) {
        if (colorNames == null || colorCodes == null) {
            return false;
        }

        final int colorNameArrayLength = colorNames.length;
        final int colorCodeArrayLength = colorCodes.length;
        final List<ColorAttributes> colorList = new ArrayList<>();
        final String[] colorCodeArray = fillColorCodeArray(colorCodes, colorNameArrayLength,
                colorCodeArrayLength);
        computeTextWidthBounds(colorNames);

        for (int index = 0; index < colorNameArrayLength; index++) {
            colorList.add(
                    new ColorAttributes(
                            /* colorName= */ colorNames[index],
                            /* colorCode= */ Color.parseColor(colorCodeArray[index]),
                            /* textBound= */ mTextBound,
                            /* gradientDrawable= */
                            new GradientDrawable(Orientation.LEFT_RIGHT, null)));
        }

        mDisplayAdapter.setColorList(colorList);
        mDisplayAdapter.notifyDataSetChanged();
        return true;
    }

    private String[] fillColorCodeArray(String[] colorCodes, int colorNameArrayLength,
            int colorCodeArrayLength) {
        if (colorNameArrayLength == colorCodeArrayLength
                || colorNameArrayLength < colorCodeArrayLength) {
            return colorCodes;
        }

        final String[] colorCodeArray = new String[colorNameArrayLength];
        for (int index = 0; index < colorNameArrayLength; index++) {
            if (index < colorCodeArrayLength) {
                colorCodeArray[index] = colorCodes[index];
            } else {
                colorCodeArray[index] = mDefaultGradientColorCodeString;
            }
        }
        return colorCodeArray;
    }

    private void computeTextWidthBounds(TypedArray colorNameTypedArray) {
        final int colorNameArrayLength = colorNameTypedArray.length();
        final String[] colorNames = new String[colorNameArrayLength];
        for (int index = 0; index < colorNameArrayLength; index++) {
            colorNames[index] = colorNameTypedArray.getString(index);
        }

        measureBound(colorNames);
    }

    private void computeTextWidthBounds(String[] colorNameArray) {
        final int colorNameArrayLength = colorNameArray.length;
        final String[] colorNames = new String[colorNameArrayLength];
        for (int index = 0; index < colorNameArrayLength; index++) {
            colorNames[index] = colorNameArray[index];
        }

        measureBound(colorNames);
    }

    private void measureBound(String[] dataArray) {
        final WindowManager windowManager = (WindowManager) mContext.getSystemService(
                Context.WINDOW_SERVICE);
        final View view = mLayoutInflater.inflate(R.layout.palette_listview_item, null);
        final TextView textView = view.findViewById(R.id.item_textview);
        final List<String> colorNameList = new ArrayList<>(Arrays.asList(dataArray));
        Collections.sort(colorNameList, Comparator.comparing(String::length));
        // Gets the last index of list which sort by text length.
        textView.setText(Iterables.getLast(colorNameList));

        final float textWidth = textView.getPaint().measureText(textView.getText().toString());
        // Computes rate of text width compare to screen width, and measures the round the double
        // to two decimal places manually.
        final float textBound = Math.round(
                textWidth / getScreenWidth(windowManager) * LANDSCAPE_MAX_WIDTH_PERCENTAGE)
                / LANDSCAPE_MAX_WIDTH_PERCENTAGE;

        // Left padding and right padding with color preview.
        final float paddingPixel = getResources().getDimension(
                R.dimen.accessibility_layout_margin_start_end);
        final float paddingWidth =
                Math.round(paddingPixel / getScreenWidth(windowManager)
                        * LANDSCAPE_MAX_WIDTH_PERCENTAGE) / LANDSCAPE_MAX_WIDTH_PERCENTAGE;
        mTextBound = textBound + paddingWidth + paddingWidth;
    }

    private static class ViewHolder {
        public TextView textView;
    }

    /** An adapter that converts color text title and color code to text views. */
    private final class DisplayAdapter extends BaseAdapter {

        private List<ColorAttributes> mColorList;

        @Override
        public int getCount() {
            return mColorList.size();
        }

        @Override
        public Object getItem(int position) {
            return mColorList.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            final ViewHolder viewHolder;
            final ColorAttributes paletteAttribute = mColorList.get(position);
            final String colorName = paletteAttribute.getColorName();
            final GradientDrawable gradientDrawable = paletteAttribute.getGradientDrawable();

            if (convertView == null) {
                convertView = mLayoutInflater.inflate(R.layout.palette_listview_item, null);
                viewHolder = new ViewHolder();
                viewHolder.textView = convertView.findViewById(R.id.item_textview);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) convertView.getTag();
            }

            viewHolder.textView.setText(colorName);
            viewHolder.textView.setBackground(gradientDrawable);
            return convertView;
        }

        protected void setColorList(List<ColorAttributes> colorList) {
            mColorList = colorList;
        }
    }

    private final class ColorAttributes {
        private final int mColorIndex = 2; // index for inject color.
        private final int mColorOffsetIndex = 1; // index for offset effect.
        private final String mColorName;
        private final GradientDrawable mGradientDrawable;
        private final int[] mGradientColors =
                {/* startColor=*/ mDefaultGradientColor, /* centerColor=*/ mDefaultGradientColor,
                        /* endCode= */ 0};
        private final float[] mGradientOffsets =
                {/* starOffset= */ 0.0f, /* centerOffset= */ 0.5f, /* endOffset= */ 1.0f};

        ColorAttributes(
                String colorName, int colorCode, float textBound,
                GradientDrawable gradientDrawable) {
            mGradientColors[mColorIndex] = colorCode;
            mGradientOffsets[mColorOffsetIndex] = textBound;
            gradientDrawable.setColors(mGradientColors, mGradientOffsets);
            mColorName = colorName;
            mGradientDrawable = gradientDrawable;
        }

        public String getColorName() {
            return mColorName;
        }

        public GradientDrawable getGradientDrawable() {
            return mGradientDrawable;
        }
    }
}
+107 −80

File changed.

Preview size limit exceeded, changes collapsed.

Loading