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