1 package com.android.customization.picker.theme; 2 3 import android.content.Context; 4 import android.content.res.ColorStateList; 5 import android.content.res.Resources; 6 import android.graphics.Typeface; 7 import android.graphics.drawable.Drawable; 8 import android.graphics.drawable.GradientDrawable; 9 import android.icu.text.DateFormat; 10 import android.view.LayoutInflater; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.view.View.OnLayoutChangeListener; 14 import android.view.ViewGroup; 15 import android.widget.CompoundButton; 16 import android.widget.ImageView; 17 import android.widget.TextView; 18 19 import androidx.annotation.ColorInt; 20 import androidx.annotation.DrawableRes; 21 import androidx.annotation.LayoutRes; 22 import androidx.annotation.StringRes; 23 import androidx.constraintlayout.widget.Guideline; 24 25 import com.android.customization.picker.BasePreviewAdapter.PreviewPage; 26 import com.android.wallpaper.R; 27 28 import java.text.FieldPosition; 29 import java.util.Calendar; 30 import java.util.List; 31 import java.util.TimeZone; 32 33 abstract class ThemePreviewPage extends PreviewPage { 34 35 public interface TimeContainer { updateTime()36 void updateTime(); 37 } 38 39 @StringRes 40 final int nameResId; 41 final Drawable icon; 42 @LayoutRes 43 final int contentLayoutRes; 44 @ColorInt 45 final int accentColor; 46 protected final LayoutInflater inflater; 47 ThemePreviewPage(Context context, @StringRes int titleResId, @DrawableRes int iconSrc, @LayoutRes int contentLayoutRes, @ColorInt int accentColor)48 public ThemePreviewPage(Context context, @StringRes int titleResId, 49 @DrawableRes int iconSrc, @LayoutRes int contentLayoutRes, 50 @ColorInt int accentColor) { 51 super(null); 52 this.nameResId = titleResId; 53 if (iconSrc != Resources.ID_NULL) { 54 this.icon = context.getResources().getDrawable(iconSrc, context.getTheme()); 55 int size = context.getResources().getDimensionPixelSize(R.dimen.card_header_icon_size); 56 icon.setBounds(0, 0, size, size); 57 } else { 58 this.icon = null; 59 } 60 this.contentLayoutRes = contentLayoutRes; 61 this.accentColor = accentColor; 62 this.inflater = LayoutInflater.from(context); 63 } 64 65 @Override bindPreviewContent()66 public void bindPreviewContent() { 67 TextView header = card.findViewById(R.id.theme_preview_card_header); 68 header.setText(nameResId); 69 header.setCompoundDrawables(null, icon, null, null); 70 header.setCompoundDrawableTintList(ColorStateList.valueOf(accentColor)); 71 card.findViewById(R.id.theme_preview_top_bar).setVisibility(View.GONE); 72 card.findViewById(R.id.edit_label).setVisibility(View.GONE); 73 74 ViewGroup body = card.findViewById(R.id.theme_preview_card_body_container); 75 inflater.inflate(contentLayoutRes, body, true); 76 bindBody(false); 77 } 78 containsWallpaper()79 protected boolean containsWallpaper() { 80 return false; 81 } 82 bindBody(boolean forceRebind)83 protected abstract void bindBody(boolean forceRebind); 84 85 static class ThemeCoverPage extends ThemePreviewPage implements TimeContainer { 86 87 public static final int COVER_PAGE_WALLPAPER_ALPHA = 0x66; 88 /** 89 * Maps which icon from ResourceConstants#ICONS_FOR_PREVIEW to use for each icon in the 90 * top bar (fake "status bar") of the cover page. 91 */ 92 private static final int [] sTopBarIconToPreviewIcon = new int [] { 0, 6, 7 }; 93 94 private final Typeface mHeadlineFont; 95 private final List<Drawable> mIcons; 96 private final List<Drawable> mShapeAppIcons; 97 private Drawable mShapeDrawable; 98 private final int[] mColorButtonIds; 99 private final int[] mColorTileIds; 100 private final int[][] mColorTileIconIds; 101 private final int[] mShapeIconIds; 102 private final Resources mRes; 103 private String mTitle; 104 private OnClickListener mEditClickListener; 105 private final OnLayoutChangeListener[] mListeners; 106 private final int mCornerRadius; 107 private final ColorStateList mTintList; 108 ThemeCoverPage(Context context, String title, int accentColor, List<Drawable> icons, Typeface headlineFont, int cornerRadius, Drawable shapeDrawable, List<Drawable> shapeAppIcons, OnClickListener editClickListener, int[] colorButtonIds, int[] colorTileIds, int[][] colorTileIconIds, int[] shapeIconIds, OnLayoutChangeListener... wallpaperListeners)109 public ThemeCoverPage(Context context, String title, int accentColor, List<Drawable> icons, 110 Typeface headlineFont, int cornerRadius, 111 Drawable shapeDrawable, 112 List<Drawable> shapeAppIcons, 113 OnClickListener editClickListener, 114 int[] colorButtonIds, int[] colorTileIds, int[][] colorTileIconIds, 115 int[] shapeIconIds, OnLayoutChangeListener... wallpaperListeners) { 116 super(context, 0, 0, R.layout.preview_card_cover_content, accentColor); 117 mRes = context.getResources(); 118 mTitle = title; 119 mHeadlineFont = headlineFont; 120 mIcons = icons; 121 mCornerRadius = cornerRadius; 122 mShapeDrawable = shapeDrawable; 123 mShapeAppIcons = shapeAppIcons; 124 mEditClickListener = editClickListener; 125 mColorButtonIds = colorButtonIds; 126 mColorTileIds = colorTileIds; 127 mColorTileIconIds = colorTileIconIds; 128 mShapeIconIds = shapeIconIds; 129 mListeners = wallpaperListeners; 130 // Color QS icons: 131 int controlGreyColor = mRes.getColor(R.color.control_grey, null); 132 mTintList = new ColorStateList( 133 new int[][]{ 134 new int[]{android.R.attr.state_selected}, 135 new int[]{android.R.attr.state_checked}, 136 new int[]{-android.R.attr.state_enabled}, 137 }, 138 new int[] { 139 accentColor, 140 accentColor, 141 controlGreyColor 142 } 143 ); 144 } 145 146 @Override bindBody(boolean forceRebind)147 protected void bindBody(boolean forceRebind) { 148 if (card == null) { 149 return; 150 } 151 if (mListeners != null) { 152 for (OnLayoutChangeListener listener : mListeners) { 153 if (listener != null) { 154 card.addOnLayoutChangeListener(listener); 155 } 156 } 157 } 158 159 if (forceRebind) { 160 card.requestLayout(); 161 } 162 163 for (int i = 0; i < mColorButtonIds.length; i++) { 164 CompoundButton button = card.findViewById(mColorButtonIds[i]); 165 if (button != null) { 166 button.setButtonTintList(mTintList); 167 } 168 } 169 for (int i = 0; i < 3 && i < mIcons.size(); i++) { 170 Drawable icon = mIcons.get(mColorTileIconIds[i][1]).getConstantState() 171 .newDrawable().mutate(); 172 Drawable bgShape = mShapeDrawable.getConstantState().newDrawable(); 173 bgShape.setTint(accentColor); 174 175 ImageView bg = card.findViewById(mColorTileIds[i]); 176 bg.setImageDrawable(bgShape); 177 ImageView fg = card.findViewById(mColorTileIconIds[i][0]); 178 fg.setImageDrawable(icon); 179 } 180 181 // Shape preview icons: 182 183 for (int i = 0; i < 3 && i < mShapeAppIcons.size(); i++) { 184 ImageView iconView = card.findViewById(mShapeIconIds[i]); 185 iconView.setBackground(mShapeAppIcons.get(i)); 186 } 187 } 188 189 @Override bindPreviewContent()190 public void bindPreviewContent() { 191 TextView header = card.findViewById(R.id.theme_preview_card_header); 192 header.setText(mTitle); 193 header.setTextAppearance(R.style.CoverTitleTextAppearance); 194 header.setTypeface(mHeadlineFont); 195 196 card.findViewById(R.id.theme_preview_top_bar).setVisibility(View.VISIBLE); 197 TextView clock = card.findViewById(R.id.theme_preview_clock); 198 clock.setText(getFormattedTime()); 199 clock.setTypeface(mHeadlineFont); 200 201 ViewGroup iconsContainer = card.findViewById(R.id.theme_preview_top_bar_icons); 202 203 for (int i = 0; i < iconsContainer.getChildCount(); i++) { 204 int iconIndex = sTopBarIconToPreviewIcon[i]; 205 if (iconIndex < mIcons.size()) { 206 ((ImageView) iconsContainer.getChildAt(i)) 207 .setImageDrawable(mIcons.get(iconIndex).getConstantState() 208 .newDrawable().mutate()); 209 } else { 210 iconsContainer.getChildAt(i).setVisibility(View.GONE); 211 } 212 } 213 214 ViewGroup body = card.findViewById(R.id.theme_preview_card_body_container); 215 216 inflater.inflate(contentLayoutRes, body, true); 217 218 bindBody(false); 219 220 TextView editLabel = card.findViewById(R.id.edit_label); 221 editLabel.setOnClickListener(mEditClickListener); 222 card.setOnClickListener(mEditClickListener); 223 card.setClickable(mEditClickListener != null); 224 225 editLabel.setVisibility(mEditClickListener != null 226 ? View.VISIBLE : View.INVISIBLE); 227 228 View qsb = card.findViewById(R.id.theme_qsb); 229 if (qsb != null && qsb.getVisibility() == View.VISIBLE) { 230 if (qsb.getBackground() instanceof GradientDrawable) { 231 GradientDrawable bg = (GradientDrawable) qsb.getBackground(); 232 float cornerRadius = useRoundedQSB(mCornerRadius) 233 ? (float)qsb.getLayoutParams().height / 2 : mCornerRadius; 234 bg.setCornerRadii(new float[]{ 235 cornerRadius, cornerRadius, cornerRadius, cornerRadius, 236 cornerRadius, cornerRadius, cornerRadius, cornerRadius}); 237 } 238 } 239 240 Guideline guideline = card.findViewById(R.id.guideline); 241 if (guideline != null) { 242 guideline.setGuidelineEnd(card.getResources().getDimensionPixelOffset( 243 R.dimen.preview_theme_cover_content_bottom)); 244 } 245 } 246 247 @Override updateTime()248 public void updateTime() { 249 if (card != null) { 250 ((TextView) card.findViewById(R.id.theme_preview_clock)).setText( 251 getFormattedTime()); 252 } 253 } 254 useRoundedQSB(int cornerRadius)255 private boolean useRoundedQSB(int cornerRadius) { 256 return cornerRadius >= 257 card.getResources().getDimensionPixelSize(R.dimen.roundCornerThreshold); 258 } 259 getFormattedTime()260 private String getFormattedTime() { 261 DateFormat df = DateFormat.getTimeInstance(DateFormat.SHORT); 262 StringBuffer time = new StringBuffer(); 263 FieldPosition amPmPosition = new FieldPosition(DateFormat.Field.AM_PM); 264 df.format(Calendar.getInstance(TimeZone.getDefault()).getTime(), time, amPmPosition); 265 if (amPmPosition.getBeginIndex() > 0) { 266 time.delete(amPmPosition.getBeginIndex(), amPmPosition.getEndIndex()); 267 } 268 return time.toString(); 269 } 270 271 @Override containsWallpaper()272 protected boolean containsWallpaper() { 273 return true; 274 } 275 } 276 } 277