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