1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.accessibility;
18 
19 import static android.graphics.drawable.GradientDrawable.Orientation;
20 
21 import static com.android.settings.accessibility.AccessibilityUtil.getScreenHeightPixels;
22 import static com.android.settings.accessibility.AccessibilityUtil.getScreenWidthPixels;
23 
24 import static com.google.common.primitives.Ints.max;
25 
26 import android.content.Context;
27 import android.graphics.Paint.FontMetrics;
28 import android.graphics.drawable.GradientDrawable;
29 import android.util.AttributeSet;
30 import android.view.Gravity;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import android.widget.TextView;
34 
35 import androidx.annotation.ColorInt;
36 import androidx.annotation.IntDef;
37 import androidx.preference.Preference;
38 import androidx.preference.PreferenceViewHolder;
39 
40 import com.android.settings.R;
41 
42 import com.google.common.primitives.Floats;
43 import com.google.common.primitives.Ints;
44 
45 import java.lang.annotation.Retention;
46 import java.lang.annotation.RetentionPolicy;
47 import java.util.ArrayList;
48 import java.util.Arrays;
49 import java.util.Collections;
50 import java.util.Comparator;
51 import java.util.List;
52 import java.util.stream.Collectors;
53 
54 /** Preference that easier preview by matching name to color. */
55 public final class PaletteListPreference extends Preference {
56 
57     private final List<Integer> mGradientColors = new ArrayList<>();
58     private final List<Float> mGradientOffsets = new ArrayList<>();
59 
60     @IntDef({
61             Position.START,
62             Position.CENTER,
63             Position.END,
64     })
65     @Retention(RetentionPolicy.SOURCE)
66     @interface Position {
67         int START = 0;
68         int CENTER = 1;
69         int END = 2;
70     }
71 
72     /**
73      * Constructs a new PaletteListPreference with the given context's theme and the supplied
74      * attribute set.
75      *
76      * @param context The Context this is associated with, through which it can access the current
77      *                theme, resources, etc.
78      * @param attrs The attributes of the XML tag that is inflating the view.
79      */
PaletteListPreference(Context context, AttributeSet attrs)80     public PaletteListPreference(Context context, AttributeSet attrs) {
81         this(context, attrs, 0);
82     }
83 
84     /**
85      * Constructs a new PaletteListPreference with the given context's theme, the supplied
86      * attribute set, and default style attribute.
87      *
88      * @param context The Context this is associated with, through which it can access the
89      *                current theme, resources, etc.
90      * @param attrs The attributes of the XML tag that is inflating the view.
91      * @param defStyleAttr An attribute in the current theme that contains a reference to a style
92      *                     resource that supplies default
93      *                     values for the view. Can be 0 to not look for
94      *                     defaults.
95      */
PaletteListPreference(Context context, AttributeSet attrs, int defStyleAttr)96     public PaletteListPreference(Context context, AttributeSet attrs, int defStyleAttr) {
97         super(context, attrs, defStyleAttr);
98         setLayoutResource(R.layout.daltonizer_preview);
99     }
100 
101     @Override
onBindViewHolder(PreferenceViewHolder holder)102     public void onBindViewHolder(PreferenceViewHolder holder) {
103         super.onBindViewHolder(holder);
104 
105         final ViewGroup paletteView = holder.itemView.findViewById(R.id.palette_view);
106         initPaletteAttributes(getContext());
107         initPaletteView(getContext(), paletteView);
108     }
109 
initPaletteAttributes(Context context)110     private void initPaletteAttributes(Context context) {
111         final int defaultColor = context.getColor(R.color.palette_list_gradient_background);
112         mGradientColors.add(Position.START, defaultColor);
113         mGradientColors.add(Position.CENTER, defaultColor);
114         mGradientColors.add(Position.END, defaultColor);
115 
116         mGradientOffsets.add(Position.START, /* element= */ 0.0f);
117         mGradientOffsets.add(Position.CENTER, /* element= */ 0.5f);
118         mGradientOffsets.add(Position.END, /* element= */ 1.0f);
119     }
120 
initPaletteView(Context context, ViewGroup rootView)121     private void initPaletteView(Context context, ViewGroup rootView) {
122         if (rootView.getChildCount() > 0) {
123             rootView.removeAllViews();
124         }
125 
126         final List<Integer> paletteColors = getPaletteColors(context);
127         final List<String> paletteData = getPaletteData(context);
128 
129         final float textPadding =
130                 context.getResources().getDimension(R.dimen.accessibility_layout_margin_start_end);
131         final String maxLengthData =
132                 Collections.max(paletteData, Comparator.comparing(String::length));
133         final int textWidth = getTextWidth(context, maxLengthData);
134         final float textBound = (textWidth + textPadding) / getScreenWidthPixels(context);
135         mGradientOffsets.set(Position.CENTER, textBound);
136 
137         final int screenHalfHeight = getScreenHeightPixels(context) / 2;
138         final int paletteItemHeight =
139                 max(screenHalfHeight / paletteData.size(), getTextLineHeight(context));
140 
141         for (int i = 0; i < paletteData.size(); ++i) {
142             final TextView textView = new TextView(context);
143             textView.setText(paletteData.get(i));
144             textView.setHeight(paletteItemHeight);
145             textView.setPaddingRelative(Math.round(textPadding), 0, 0, 0);
146             textView.setGravity(Gravity.CENTER_VERTICAL);
147             textView.setBackground(createGradientDrawable(rootView, paletteColors.get(i)));
148 
149             rootView.addView(textView);
150         }
151     }
152 
createGradientDrawable(ViewGroup rootView, @ColorInt int color)153     private GradientDrawable createGradientDrawable(ViewGroup rootView, @ColorInt int color) {
154         mGradientColors.set(Position.END, color);
155 
156         final GradientDrawable gradientDrawable = new GradientDrawable();
157         final Orientation orientation =
158                 rootView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
159                         ? Orientation.RIGHT_LEFT
160                         : Orientation.LEFT_RIGHT;
161         gradientDrawable.setOrientation(orientation);
162         gradientDrawable.setColors(Ints.toArray(mGradientColors), Floats.toArray(mGradientOffsets));
163 
164         return gradientDrawable;
165     }
166 
getPaletteColors(Context context)167     private List<Integer> getPaletteColors(Context context) {
168         final int[] paletteResources =
169                 context.getResources().getIntArray(R.array.setting_palette_colors);
170         return Arrays.stream(paletteResources).boxed().collect(Collectors.toList());
171     }
172 
getPaletteData(Context context)173     private List<String> getPaletteData(Context context) {
174         final String[] paletteResources =
175                 context.getResources().getStringArray(R.array.setting_palette_data);
176         return Arrays.asList(paletteResources);
177     }
178 
getTextWidth(Context context, String text)179     private int getTextWidth(Context context, String text) {
180         final TextView tempView = new TextView(context);
181         return Math.round(tempView.getPaint().measureText(text));
182     }
183 
getTextLineHeight(Context context)184     private int getTextLineHeight(Context context) {
185         final TextView tempView = new TextView(context);
186         final FontMetrics fontMetrics = tempView.getPaint().getFontMetrics();
187         return Math.round(fontMetrics.bottom - fontMetrics.top);
188     }
189 }
190