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.content.res.ColorStateList; 28 import android.graphics.Paint.FontMetrics; 29 import android.graphics.drawable.GradientDrawable; 30 import android.util.AttributeSet; 31 import android.view.Gravity; 32 import android.view.View; 33 import android.view.ViewGroup; 34 import android.widget.TextView; 35 36 import androidx.annotation.ColorInt; 37 import androidx.annotation.IntDef; 38 import androidx.core.text.TextUtilsCompat; 39 import androidx.preference.Preference; 40 import androidx.preference.PreferenceViewHolder; 41 42 import com.android.settings.R; 43 import com.android.settingslib.Utils; 44 45 import com.google.common.primitives.Floats; 46 import com.google.common.primitives.Ints; 47 48 import java.lang.annotation.Retention; 49 import java.lang.annotation.RetentionPolicy; 50 import java.util.ArrayList; 51 import java.util.Arrays; 52 import java.util.Collections; 53 import java.util.Comparator; 54 import java.util.List; 55 import java.util.Locale; 56 import java.util.stream.Collectors; 57 58 /** Preference that easier preview by matching name to color. */ 59 public final class PaletteListPreference extends Preference { 60 61 private final List<Integer> mGradientColors = new ArrayList<>(); 62 private final List<Float> mGradientOffsets = new ArrayList<>(); 63 64 @IntDef({ 65 Position.START, 66 Position.CENTER, 67 Position.END, 68 }) 69 @Retention(RetentionPolicy.SOURCE) 70 @interface Position { 71 int START = 0; 72 int CENTER = 1; 73 int END = 2; 74 } 75 76 /** 77 * Constructs a new PaletteListPreference with the given context's theme and the supplied 78 * attribute set. 79 * 80 * @param context The Context this is associated with, through which it can access the current 81 * theme, resources, etc. 82 * @param attrs The attributes of the XML tag that is inflating the view. 83 */ PaletteListPreference(Context context, AttributeSet attrs)84 public PaletteListPreference(Context context, AttributeSet attrs) { 85 this(context, attrs, 0); 86 } 87 88 /** 89 * Constructs a new PaletteListPreference with the given context's theme, the supplied 90 * attribute set, and default style attribute. 91 * 92 * @param context The Context this is associated with, through which it can access the 93 * current theme, resources, etc. 94 * @param attrs The attributes of the XML tag that is inflating the view. 95 * @param defStyleAttr An attribute in the current theme that contains a reference to a style 96 * resource that supplies default 97 * values for the view. Can be 0 to not look for 98 * defaults. 99 */ PaletteListPreference(Context context, AttributeSet attrs, int defStyleAttr)100 public PaletteListPreference(Context context, AttributeSet attrs, int defStyleAttr) { 101 super(context, attrs, defStyleAttr); 102 setLayoutResource(R.layout.daltonizer_preview); 103 } 104 105 @Override onBindViewHolder(PreferenceViewHolder holder)106 public void onBindViewHolder(PreferenceViewHolder holder) { 107 super.onBindViewHolder(holder); 108 109 final ViewGroup paletteView = holder.itemView.findViewById(R.id.palette_view); 110 initPaletteAttributes(getContext()); 111 initPaletteView(getContext(), paletteView); 112 } 113 initPaletteAttributes(Context context)114 private void initPaletteAttributes(Context context) { 115 final int defaultColor = context.getColor(R.color.palette_list_gradient_background); 116 mGradientColors.add(Position.START, defaultColor); 117 mGradientColors.add(Position.CENTER, defaultColor); 118 mGradientColors.add(Position.END, defaultColor); 119 120 mGradientOffsets.add(Position.START, /* element= */ 0.0f); 121 mGradientOffsets.add(Position.CENTER, /* element= */ 0.5f); 122 mGradientOffsets.add(Position.END, /* element= */ 1.0f); 123 } 124 initPaletteView(Context context, ViewGroup rootView)125 private void initPaletteView(Context context, ViewGroup rootView) { 126 if (rootView.getChildCount() > 0) { 127 rootView.removeAllViews(); 128 } 129 130 final List<Integer> paletteColors = getPaletteColors(context); 131 final List<String> paletteData = getPaletteData(context); 132 133 final ColorStateList textColor = 134 Utils.getColorAttr(getContext(), android.R.attr.textColorPrimary); 135 final float textPadding = 136 context.getResources().getDimension(R.dimen.accessibility_layout_margin_start_end); 137 final String maxLengthData = 138 Collections.max(paletteData, Comparator.comparing(String::length)); 139 final int textWidth = getTextWidth(context, maxLengthData); 140 final float textBound = (textWidth + textPadding) / getScreenWidthPixels(context); 141 mGradientOffsets.set(Position.CENTER, textBound); 142 143 final int screenHalfHeight = getScreenHeightPixels(context) / 2; 144 final int paletteItemHeight = 145 max(screenHalfHeight / paletteData.size(), getTextLineHeight(context)); 146 147 for (int i = 0; i < paletteData.size(); ++i) { 148 final TextView textView = new TextView(context); 149 textView.setText(paletteData.get(i)); 150 textView.setTextColor(textColor); 151 textView.setHeight(paletteItemHeight); 152 textView.setPaddingRelative(Math.round(textPadding), 0, 0, 0); 153 textView.setGravity(Gravity.CENTER_VERTICAL); 154 textView.setBackground(createGradientDrawable(rootView, paletteColors.get(i))); 155 156 rootView.addView(textView); 157 } 158 159 updateFirstAndLastItemsBackground(context, rootView, paletteData.size()); 160 } 161 createGradientDrawable(ViewGroup rootView, @ColorInt int color)162 private GradientDrawable createGradientDrawable(ViewGroup rootView, @ColorInt int color) { 163 mGradientColors.set(Position.END, color); 164 165 final GradientDrawable gradientDrawable = new GradientDrawable(); 166 final Locale locale = Locale.getDefault(); 167 final Orientation orientation = 168 TextUtilsCompat.getLayoutDirectionFromLocale(locale) == View.LAYOUT_DIRECTION_RTL 169 ? Orientation.RIGHT_LEFT 170 : Orientation.LEFT_RIGHT; 171 gradientDrawable.setOrientation(orientation); 172 gradientDrawable.setColors(Ints.toArray(mGradientColors), Floats.toArray(mGradientOffsets)); 173 174 return gradientDrawable; 175 } 176 updateFirstAndLastItemsBackground(Context context, ViewGroup rootView, int size)177 private void updateFirstAndLastItemsBackground(Context context, ViewGroup rootView, int size) { 178 final int radius = 179 context.getResources().getDimensionPixelSize( 180 R.dimen.accessibility_illustration_view_radius); 181 final int lastIndex = size - 1; 182 final GradientDrawable firstItem = 183 (GradientDrawable) rootView.getChildAt(0).getBackground(); 184 final GradientDrawable lastItem = 185 (GradientDrawable) rootView.getChildAt(lastIndex).getBackground(); 186 firstItem.setCornerRadii(new float[]{radius, radius, radius, radius, 0, 0, 0, 0}); 187 lastItem.setCornerRadii(new float[]{0, 0, 0, 0, radius, radius, radius, radius}); 188 } 189 getPaletteColors(Context context)190 private List<Integer> getPaletteColors(Context context) { 191 final int[] paletteResources = 192 context.getResources().getIntArray(R.array.setting_palette_colors); 193 return Arrays.stream(paletteResources).boxed().collect(Collectors.toList()); 194 } 195 getPaletteData(Context context)196 private List<String> getPaletteData(Context context) { 197 final String[] paletteResources = 198 context.getResources().getStringArray(R.array.setting_palette_data); 199 return Arrays.asList(paletteResources); 200 } 201 getTextWidth(Context context, String text)202 private int getTextWidth(Context context, String text) { 203 final TextView tempView = new TextView(context); 204 return Math.round(tempView.getPaint().measureText(text)); 205 } 206 getTextLineHeight(Context context)207 private int getTextLineHeight(Context context) { 208 final TextView tempView = new TextView(context); 209 final FontMetrics fontMetrics = tempView.getPaint().getFontMetrics(); 210 return Math.round(fontMetrics.bottom - fontMetrics.top); 211 } 212 } 213