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