1 /*
2  * Copyright (C) 2017 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.example.android.wearable.watchface.config;
18 
19 import static com.example.android.wearable.watchface.config.ColorSelectionActivity.EXTRA_SHARED_PREF;
20 
21 import android.app.Activity;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.SharedPreferences;
26 import android.graphics.Color;
27 import android.graphics.PorterDuff;
28 import android.graphics.PorterDuffColorFilter;
29 import android.graphics.drawable.Drawable;
30 import android.support.wearable.complications.ComplicationHelperActivity;
31 import android.support.wearable.complications.ComplicationProviderInfo;
32 import android.support.wearable.complications.ProviderInfoRetriever;
33 import android.support.wearable.complications.ProviderInfoRetriever.OnProviderInfoReceivedCallback;
34 import android.util.Log;
35 import android.view.Gravity;
36 import android.view.LayoutInflater;
37 import android.view.View;
38 import android.view.View.OnClickListener;
39 import android.view.ViewGroup;
40 import android.widget.Button;
41 import android.widget.ImageButton;
42 import android.widget.ImageView;
43 import android.widget.Switch;
44 import android.widget.Toast;
45 
46 import androidx.annotation.Nullable;
47 import androidx.recyclerview.widget.RecyclerView;
48 
49 import com.example.android.wearable.watchface.R;
50 import com.example.android.wearable.watchface.model.AnalogComplicationConfigData.BackgroundComplicationConfigItem;
51 import com.example.android.wearable.watchface.model.AnalogComplicationConfigData.ColorConfigItem;
52 import com.example.android.wearable.watchface.model.AnalogComplicationConfigData.ConfigItemType;
53 import com.example.android.wearable.watchface.model.AnalogComplicationConfigData.MoreOptionsConfigItem;
54 import com.example.android.wearable.watchface.model.AnalogComplicationConfigData.PreviewAndComplicationsConfigItem;
55 import com.example.android.wearable.watchface.model.AnalogComplicationConfigData.UnreadNotificationConfigItem;
56 import com.example.android.wearable.watchface.watchface.AnalogComplicationWatchFaceService;
57 
58 import java.util.ArrayList;
59 import java.util.concurrent.Executors;
60 
61 /**
62  * Displays different layouts for configuring watch face's complications and appearance settings
63  * (highlight color [second arm], background color, unread notifications, etc.).
64  *
65  * <p>All appearance settings are saved via {@link SharedPreferences}.
66  *
67  * <p>Layouts provided by this adapter are split into 5 main view types.
68  *
69  * <p>A watch face preview including complications. Allows user to tap on the complications to
70  * change the complication data and see a live preview of the watch face.
71  *
72  * <p>Simple arrow to indicate there are more options below the fold.
73  *
74  * <p>Color configuration options for both highlight (seconds hand) and background color.
75  *
76  * <p>Toggle for unread notifications.
77  *
78  * <p>Background image complication configuration for changing background image of watch face.
79  */
80 public class AnalogComplicationConfigRecyclerViewAdapter
81         extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
82 
83     private static final String TAG = "CompConfigAdapter";
84 
85     /**
86      * Used by associated watch face ({@link AnalogComplicationWatchFaceService}) to let this
87      * adapter know which complication locations are supported, their ids, and supported
88      * complication data types.
89      */
90     public enum ComplicationLocation {
91         BACKGROUND,
92         LEFT,
93         RIGHT,
94         TOP,
95         BOTTOM
96     }
97 
98     public static final int TYPE_PREVIEW_AND_COMPLICATIONS_CONFIG = 0;
99     public static final int TYPE_MORE_OPTIONS = 1;
100     public static final int TYPE_COLOR_CONFIG = 2;
101     public static final int TYPE_UNREAD_NOTIFICATION_CONFIG = 3;
102     public static final int TYPE_BACKGROUND_COMPLICATION_IMAGE_CONFIG = 4;
103 
104     // ComponentName associated with watch face service (service that renders watch face). Used
105     // to retrieve complication information.
106     private ComponentName mWatchFaceComponentName;
107 
108     private ArrayList<ConfigItemType> mSettingsDataSet;
109 
110     private Context mContext;
111 
112     SharedPreferences mSharedPref;
113 
114     // Selected complication id by user.
115     private int mSelectedComplicationId;
116 
117     private int mBackgroundComplicationId;
118     private int mLeftComplicationId;
119     private int mRightComplicationId;
120 
121     // Required to retrieve complication data from watch face for preview.
122     private ProviderInfoRetriever mProviderInfoRetriever;
123 
124     // Maintains reference view holder to dynamically update watch face preview. Used instead of
125     // notifyItemChanged(int position) to avoid flicker and re-inflating the view.
126     private PreviewAndComplicationsViewHolder mPreviewAndComplicationsViewHolder;
127 
AnalogComplicationConfigRecyclerViewAdapter( Context context, Class watchFaceServiceClass, ArrayList<ConfigItemType> settingsDataSet)128     public AnalogComplicationConfigRecyclerViewAdapter(
129             Context context,
130             Class watchFaceServiceClass,
131             ArrayList<ConfigItemType> settingsDataSet) {
132 
133         mContext = context;
134         mWatchFaceComponentName = new ComponentName(mContext, watchFaceServiceClass);
135         mSettingsDataSet = settingsDataSet;
136 
137         // Default value is invalid (only changed when user taps to change complication).
138         mSelectedComplicationId = -1;
139 
140         mBackgroundComplicationId =
141                 AnalogComplicationWatchFaceService.getComplicationId(
142                         ComplicationLocation.BACKGROUND);
143 
144         mLeftComplicationId =
145                 AnalogComplicationWatchFaceService.getComplicationId(ComplicationLocation.LEFT);
146         mRightComplicationId =
147                 AnalogComplicationWatchFaceService.getComplicationId(ComplicationLocation.RIGHT);
148 
149         mSharedPref =
150                 context.getSharedPreferences(
151                         context.getString(R.string.analog_complication_preference_file_key),
152                         Context.MODE_PRIVATE);
153 
154         // Initialization of code to retrieve active complication data for the watch face.
155         mProviderInfoRetriever =
156                 new ProviderInfoRetriever(mContext, Executors.newCachedThreadPool());
157         mProviderInfoRetriever.init();
158     }
159 
160     @Override
onCreateViewHolder(ViewGroup parent, int viewType)161     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
162         Log.d(TAG, "onCreateViewHolder(): viewType: " + viewType);
163 
164         RecyclerView.ViewHolder viewHolder = null;
165 
166         switch (viewType) {
167             case TYPE_PREVIEW_AND_COMPLICATIONS_CONFIG:
168                 // Need direct reference to watch face preview view holder to update watch face
169                 // preview based on selections from the user.
170                 mPreviewAndComplicationsViewHolder =
171                         new PreviewAndComplicationsViewHolder(
172                                 LayoutInflater.from(parent.getContext())
173                                         .inflate(
174                                                 R.layout.config_list_preview_and_complications_item,
175                                                 parent,
176                                                 false));
177                 viewHolder = mPreviewAndComplicationsViewHolder;
178                 break;
179 
180             case TYPE_MORE_OPTIONS:
181                 viewHolder =
182                         new MoreOptionsViewHolder(
183                                 LayoutInflater.from(parent.getContext())
184                                         .inflate(
185                                                 R.layout.config_list_more_options_item,
186                                                 parent,
187                                                 false));
188                 break;
189 
190             case TYPE_COLOR_CONFIG:
191                 viewHolder =
192                         new ColorPickerViewHolder(
193                                 LayoutInflater.from(parent.getContext())
194                                         .inflate(R.layout.config_list_color_item, parent, false));
195                 break;
196 
197             case TYPE_UNREAD_NOTIFICATION_CONFIG:
198                 viewHolder =
199                         new UnreadNotificationViewHolder(
200                                 LayoutInflater.from(parent.getContext())
201                                         .inflate(
202                                                 R.layout.config_list_unread_notif_item,
203                                                 parent,
204                                                 false));
205                 break;
206 
207             case TYPE_BACKGROUND_COMPLICATION_IMAGE_CONFIG:
208                 viewHolder =
209                         new BackgroundComplicationViewHolder(
210                                 LayoutInflater.from(parent.getContext())
211                                         .inflate(
212                                                 R.layout.config_list_background_complication_item,
213                                                 parent,
214                                                 false));
215                 break;
216         }
217 
218         return viewHolder;
219     }
220 
221     @Override
onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position)222     public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
223         Log.d(TAG, "Element " + position + " set.");
224 
225         // Pulls all data required for creating the UX for the specific setting option.
226         ConfigItemType configItemType = mSettingsDataSet.get(position);
227 
228         switch (viewHolder.getItemViewType()) {
229             case TYPE_PREVIEW_AND_COMPLICATIONS_CONFIG:
230                 PreviewAndComplicationsViewHolder previewAndComplicationsViewHolder =
231                         (PreviewAndComplicationsViewHolder) viewHolder;
232 
233                 PreviewAndComplicationsConfigItem previewAndComplicationsConfigItem =
234                         (PreviewAndComplicationsConfigItem) configItemType;
235 
236                 int defaultComplicationResourceId =
237                         previewAndComplicationsConfigItem.getDefaultComplicationResourceId();
238                 previewAndComplicationsViewHolder.setDefaultComplicationDrawable(
239                         defaultComplicationResourceId);
240 
241                 previewAndComplicationsViewHolder.initializesColorsAndComplications();
242                 break;
243 
244             case TYPE_MORE_OPTIONS:
245                 MoreOptionsViewHolder moreOptionsViewHolder = (MoreOptionsViewHolder) viewHolder;
246                 MoreOptionsConfigItem moreOptionsConfigItem =
247                         (MoreOptionsConfigItem) configItemType;
248 
249                 moreOptionsViewHolder.setIcon(moreOptionsConfigItem.getIconResourceId());
250                 break;
251 
252             case TYPE_COLOR_CONFIG:
253                 ColorPickerViewHolder colorPickerViewHolder = (ColorPickerViewHolder) viewHolder;
254                 ColorConfigItem colorConfigItem = (ColorConfigItem) configItemType;
255 
256                 int iconResourceId = colorConfigItem.getIconResourceId();
257                 String name = colorConfigItem.getName();
258                 String sharedPrefString = colorConfigItem.getSharedPrefString();
259                 Class<ColorSelectionActivity> activity =
260                         colorConfigItem.getActivityToChoosePreference();
261 
262                 colorPickerViewHolder.setIcon(iconResourceId);
263                 colorPickerViewHolder.setName(name);
264                 colorPickerViewHolder.setSharedPrefString(sharedPrefString);
265                 colorPickerViewHolder.setLaunchActivityToSelectColor(activity);
266                 break;
267 
268             case TYPE_UNREAD_NOTIFICATION_CONFIG:
269                 UnreadNotificationViewHolder unreadViewHolder =
270                         (UnreadNotificationViewHolder) viewHolder;
271 
272                 UnreadNotificationConfigItem unreadConfigItem =
273                         (UnreadNotificationConfigItem) configItemType;
274 
275                 int unreadEnabledIconResourceId = unreadConfigItem.getIconEnabledResourceId();
276                 int unreadDisabledIconResourceId = unreadConfigItem.getIconDisabledResourceId();
277 
278                 String unreadName = unreadConfigItem.getName();
279                 int unreadSharedPrefId = unreadConfigItem.getSharedPrefId();
280 
281                 unreadViewHolder.setIcons(
282                         unreadEnabledIconResourceId, unreadDisabledIconResourceId);
283                 unreadViewHolder.setName(unreadName);
284                 unreadViewHolder.setSharedPrefId(unreadSharedPrefId);
285                 break;
286 
287             case TYPE_BACKGROUND_COMPLICATION_IMAGE_CONFIG:
288                 BackgroundComplicationViewHolder backgroundComplicationViewHolder =
289                         (BackgroundComplicationViewHolder) viewHolder;
290 
291                 BackgroundComplicationConfigItem backgroundComplicationConfigItem =
292                         (BackgroundComplicationConfigItem) configItemType;
293 
294                 int backgroundIconResourceId = backgroundComplicationConfigItem.getIconResourceId();
295                 String backgroundName = backgroundComplicationConfigItem.getName();
296 
297                 backgroundComplicationViewHolder.setIcon(backgroundIconResourceId);
298                 backgroundComplicationViewHolder.setName(backgroundName);
299                 break;
300         }
301     }
302 
303     @Override
getItemViewType(int position)304     public int getItemViewType(int position) {
305         ConfigItemType configItemType = mSettingsDataSet.get(position);
306         return configItemType.getConfigType();
307     }
308 
309     @Override
getItemCount()310     public int getItemCount() {
311         return mSettingsDataSet.size();
312     }
313 
314     /** Updates the selected complication id saved earlier with the new information. */
updateSelectedComplication(ComplicationProviderInfo complicationProviderInfo)315     public void updateSelectedComplication(ComplicationProviderInfo complicationProviderInfo) {
316 
317         Log.d(TAG, "updateSelectedComplication: " + mPreviewAndComplicationsViewHolder);
318 
319         // Checks if view is inflated and complication id is valid.
320         if (mPreviewAndComplicationsViewHolder != null && mSelectedComplicationId >= 0) {
321             mPreviewAndComplicationsViewHolder.updateComplicationViews(
322                     mSelectedComplicationId, complicationProviderInfo);
323         }
324     }
325 
326     @Override
onDetachedFromRecyclerView(RecyclerView recyclerView)327     public void onDetachedFromRecyclerView(RecyclerView recyclerView) {
328         super.onDetachedFromRecyclerView(recyclerView);
329         // Required to release retriever for active complication data on detach.
330         mProviderInfoRetriever.release();
331     }
332 
updatePreviewColors()333     public void updatePreviewColors() {
334         Log.d(TAG, "updatePreviewColors(): " + mPreviewAndComplicationsViewHolder);
335 
336         if (mPreviewAndComplicationsViewHolder != null) {
337             mPreviewAndComplicationsViewHolder.updateWatchFaceColors();
338         }
339     }
340 
341     /**
342      * Displays watch face preview along with complication locations. Allows user to tap on the
343      * complication they want to change and preview updates dynamically.
344      */
345     public class PreviewAndComplicationsViewHolder extends RecyclerView.ViewHolder
346             implements OnClickListener {
347 
348         private View mWatchFaceArmsAndTicksView;
349         private View mWatchFaceHighlightPreviewView;
350         private ImageView mWatchFaceBackgroundPreviewImageView;
351 
352         private ImageView mLeftComplicationBackground;
353         private ImageView mRightComplicationBackground;
354 
355         private ImageButton mLeftComplication;
356         private ImageButton mRightComplication;
357 
358         private Drawable mDefaultComplicationDrawable;
359 
360         private boolean mBackgroundComplicationEnabled;
361 
PreviewAndComplicationsViewHolder(final View view)362         public PreviewAndComplicationsViewHolder(final View view) {
363             super(view);
364 
365             mWatchFaceBackgroundPreviewImageView =
366                     (ImageView) view.findViewById(R.id.watch_face_background);
367             mWatchFaceArmsAndTicksView = view.findViewById(R.id.watch_face_arms_and_ticks);
368 
369             // In our case, just the second arm.
370             mWatchFaceHighlightPreviewView = view.findViewById(R.id.watch_face_highlight);
371 
372             // Sets up left complication preview.
373             mLeftComplicationBackground =
374                     (ImageView) view.findViewById(R.id.left_complication_background);
375             mLeftComplication = (ImageButton) view.findViewById(R.id.left_complication);
376             mLeftComplication.setOnClickListener(this);
377 
378             // Sets up right complication preview.
379             mRightComplicationBackground =
380                     (ImageView) view.findViewById(R.id.right_complication_background);
381             mRightComplication = (ImageButton) view.findViewById(R.id.right_complication);
382             mRightComplication.setOnClickListener(this);
383         }
384 
385         @Override
onClick(View view)386         public void onClick(View view) {
387             if (view.equals(mLeftComplication)) {
388                 Log.d(TAG, "Left Complication click()");
389 
390                 Activity currentActivity = (Activity) view.getContext();
391                 launchComplicationHelperActivity(currentActivity, ComplicationLocation.LEFT);
392 
393             } else if (view.equals(mRightComplication)) {
394                 Log.d(TAG, "Right Complication click()");
395 
396                 Activity currentActivity = (Activity) view.getContext();
397                 launchComplicationHelperActivity(currentActivity, ComplicationLocation.RIGHT);
398             }
399         }
400 
updateWatchFaceColors()401         public void updateWatchFaceColors() {
402 
403             // Only update background colors for preview if background complications are disabled.
404             if (!mBackgroundComplicationEnabled) {
405                 // Updates background color.
406                 String backgroundSharedPrefString =
407                         mContext.getString(R.string.saved_background_color);
408                 int currentBackgroundColor =
409                         mSharedPref.getInt(backgroundSharedPrefString, Color.BLACK);
410 
411                 PorterDuffColorFilter backgroundColorFilter =
412                         new PorterDuffColorFilter(currentBackgroundColor, PorterDuff.Mode.SRC_ATOP);
413 
414                 mWatchFaceBackgroundPreviewImageView
415                         .getBackground()
416                         .setColorFilter(backgroundColorFilter);
417 
418             } else {
419                 // Inform user that they need to disable background image for color to work.
420                 CharSequence text = "Selected image overrides background color.";
421                 int duration = Toast.LENGTH_SHORT;
422                 Toast toast = Toast.makeText(mContext, text, duration);
423                 toast.setGravity(Gravity.CENTER, 0, 0);
424                 toast.show();
425             }
426 
427             // Updates highlight color (just second arm).
428             String highlightSharedPrefString = mContext.getString(R.string.saved_marker_color);
429             int currentHighlightColor = mSharedPref.getInt(highlightSharedPrefString, Color.RED);
430 
431             PorterDuffColorFilter highlightColorFilter =
432                     new PorterDuffColorFilter(currentHighlightColor, PorterDuff.Mode.SRC_ATOP);
433 
434             mWatchFaceHighlightPreviewView.getBackground().setColorFilter(highlightColorFilter);
435         }
436 
437         // Verifies the watch face supports the complication location, then launches the helper
438         // class, so user can choose their complication data provider.
launchComplicationHelperActivity( Activity currentActivity, ComplicationLocation complicationLocation)439         private void launchComplicationHelperActivity(
440                 Activity currentActivity, ComplicationLocation complicationLocation) {
441 
442             mSelectedComplicationId =
443                     AnalogComplicationWatchFaceService.getComplicationId(complicationLocation);
444 
445             mBackgroundComplicationEnabled = false;
446 
447             if (mSelectedComplicationId >= 0) {
448 
449                 int[] supportedTypes =
450                         AnalogComplicationWatchFaceService.getSupportedComplicationTypes(
451                                 complicationLocation);
452 
453                 ComponentName watchFace =
454                         new ComponentName(
455                                 currentActivity, AnalogComplicationWatchFaceService.class);
456 
457                 currentActivity.startActivityForResult(
458                         ComplicationHelperActivity.createProviderChooserHelperIntent(
459                                 currentActivity,
460                                 watchFace,
461                                 mSelectedComplicationId,
462                                 supportedTypes),
463                         AnalogComplicationConfigActivity.COMPLICATION_CONFIG_REQUEST_CODE);
464 
465             } else {
466                 Log.d(TAG, "Complication not supported by watch face.");
467             }
468         }
469 
setDefaultComplicationDrawable(int resourceId)470         public void setDefaultComplicationDrawable(int resourceId) {
471             Context context = mWatchFaceArmsAndTicksView.getContext();
472             mDefaultComplicationDrawable = context.getDrawable(resourceId);
473 
474             mLeftComplication.setImageDrawable(mDefaultComplicationDrawable);
475             mLeftComplicationBackground.setVisibility(View.INVISIBLE);
476 
477             mRightComplication.setImageDrawable(mDefaultComplicationDrawable);
478             mRightComplicationBackground.setVisibility(View.INVISIBLE);
479         }
480 
updateComplicationViews( int watchFaceComplicationId, ComplicationProviderInfo complicationProviderInfo)481         public void updateComplicationViews(
482                 int watchFaceComplicationId, ComplicationProviderInfo complicationProviderInfo) {
483             Log.d(TAG, "updateComplicationViews(): id: " + watchFaceComplicationId);
484             Log.d(TAG, "\tinfo: " + complicationProviderInfo);
485 
486             if (watchFaceComplicationId == mBackgroundComplicationId) {
487                 if (complicationProviderInfo != null) {
488                     mBackgroundComplicationEnabled = true;
489 
490                     // Since we can't get the background complication image outside of the
491                     // watch face, we set the icon for that provider instead with a gray background.
492                     PorterDuffColorFilter backgroundColorFilter =
493                             new PorterDuffColorFilter(Color.GRAY, PorterDuff.Mode.SRC_ATOP);
494 
495                     mWatchFaceBackgroundPreviewImageView
496                             .getBackground()
497                             .setColorFilter(backgroundColorFilter);
498                     mWatchFaceBackgroundPreviewImageView.setImageIcon(
499                             complicationProviderInfo.providerIcon);
500 
501                 } else {
502                     mBackgroundComplicationEnabled = false;
503 
504                     // Clears icon for background if it was present before.
505                     mWatchFaceBackgroundPreviewImageView.setImageResource(
506                             android.R.color.transparent);
507                     String backgroundSharedPrefString =
508                             mContext.getString(R.string.saved_background_color);
509                     int currentBackgroundColor =
510                             mSharedPref.getInt(backgroundSharedPrefString, Color.BLACK);
511 
512                     PorterDuffColorFilter backgroundColorFilter =
513                             new PorterDuffColorFilter(
514                                     currentBackgroundColor, PorterDuff.Mode.SRC_ATOP);
515 
516                     mWatchFaceBackgroundPreviewImageView
517                             .getBackground()
518                             .setColorFilter(backgroundColorFilter);
519                 }
520 
521             } else if (watchFaceComplicationId == mLeftComplicationId) {
522                 updateComplicationView(complicationProviderInfo, mLeftComplication,
523                     mLeftComplicationBackground);
524 
525             } else if (watchFaceComplicationId == mRightComplicationId) {
526                 updateComplicationView(complicationProviderInfo, mRightComplication,
527                     mRightComplicationBackground);
528             }
529         }
530 
updateComplicationView(ComplicationProviderInfo complicationProviderInfo, ImageButton button, ImageView background)531         private void updateComplicationView(ComplicationProviderInfo complicationProviderInfo,
532             ImageButton button, ImageView background) {
533             if (complicationProviderInfo != null) {
534                 button.setImageIcon(complicationProviderInfo.providerIcon);
535                 button.setContentDescription(
536                     mContext.getString(R.string.edit_complication,
537                         complicationProviderInfo.appName + " " +
538                             complicationProviderInfo.providerName));
539                 background.setVisibility(View.VISIBLE);
540             } else {
541                 button.setImageDrawable(mDefaultComplicationDrawable);
542                 button.setContentDescription(mContext.getString(R.string.add_complication));
543                 background.setVisibility(View.INVISIBLE);
544             }
545         }
546 
initializesColorsAndComplications()547         public void initializesColorsAndComplications() {
548 
549             // Initializes highlight color (just second arm and part of complications).
550             String highlightSharedPrefString = mContext.getString(R.string.saved_marker_color);
551             int currentHighlightColor = mSharedPref.getInt(highlightSharedPrefString, Color.RED);
552 
553             PorterDuffColorFilter highlightColorFilter =
554                     new PorterDuffColorFilter(currentHighlightColor, PorterDuff.Mode.SRC_ATOP);
555 
556             mWatchFaceHighlightPreviewView.getBackground().setColorFilter(highlightColorFilter);
557 
558             // Initializes background color to gray (updates to color or complication icon based
559             // on whether the background complication is live or not.
560             PorterDuffColorFilter backgroundColorFilter =
561                     new PorterDuffColorFilter(Color.GRAY, PorterDuff.Mode.SRC_ATOP);
562 
563             mWatchFaceBackgroundPreviewImageView
564                     .getBackground()
565                     .setColorFilter(backgroundColorFilter);
566 
567             final int[] complicationIds = AnalogComplicationWatchFaceService.getComplicationIds();
568 
569             mProviderInfoRetriever.retrieveProviderInfo(
570                     new OnProviderInfoReceivedCallback() {
571                         @Override
572                         public void onProviderInfoReceived(
573                                 int watchFaceComplicationId,
574                                 @Nullable ComplicationProviderInfo complicationProviderInfo) {
575 
576                             Log.d(TAG, "onProviderInfoReceived: " + complicationProviderInfo);
577 
578                             updateComplicationViews(
579                                     watchFaceComplicationId, complicationProviderInfo);
580                         }
581                     },
582                     mWatchFaceComponentName,
583                     complicationIds);
584         }
585     }
586 
587     /** Displays icon to indicate there are more options below the fold. */
588     public class MoreOptionsViewHolder extends RecyclerView.ViewHolder {
589 
590         private ImageView mMoreOptionsImageView;
591 
MoreOptionsViewHolder(View view)592         public MoreOptionsViewHolder(View view) {
593             super(view);
594             mMoreOptionsImageView = (ImageView) view.findViewById(R.id.more_options_image_view);
595         }
596 
setIcon(int resourceId)597         public void setIcon(int resourceId) {
598             Context context = mMoreOptionsImageView.getContext();
599             mMoreOptionsImageView.setImageDrawable(context.getDrawable(resourceId));
600         }
601     }
602 
603     /**
604      * Displays color options for the an item on the watch face. These could include marker color,
605      * background color, etc.
606      */
607     public class ColorPickerViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
608 
609         private Button mAppearanceButton;
610 
611         private String mSharedPrefResourceString;
612 
613         private Class<ColorSelectionActivity> mLaunchActivityToSelectColor;
614 
ColorPickerViewHolder(View view)615         public ColorPickerViewHolder(View view) {
616             super(view);
617 
618             mAppearanceButton = (Button) view.findViewById(R.id.color_picker_button);
619             view.setOnClickListener(this);
620         }
621 
setName(String name)622         public void setName(String name) {
623             mAppearanceButton.setText(name);
624         }
625 
setIcon(int resourceId)626         public void setIcon(int resourceId) {
627             Context context = mAppearanceButton.getContext();
628             mAppearanceButton.setCompoundDrawablesWithIntrinsicBounds(
629                     context.getDrawable(resourceId), null, null, null);
630         }
631 
setSharedPrefString(String sharedPrefString)632         public void setSharedPrefString(String sharedPrefString) {
633             mSharedPrefResourceString = sharedPrefString;
634         }
635 
setLaunchActivityToSelectColor(Class<ColorSelectionActivity> activity)636         public void setLaunchActivityToSelectColor(Class<ColorSelectionActivity> activity) {
637             mLaunchActivityToSelectColor = activity;
638         }
639 
640         @Override
onClick(View view)641         public void onClick(View view) {
642             int position = getAdapterPosition();
643             Log.d(TAG, "Complication onClick() position: " + position);
644 
645             if (mLaunchActivityToSelectColor != null) {
646                 Intent launchIntent = new Intent(view.getContext(), mLaunchActivityToSelectColor);
647 
648                 // Pass shared preference name to save color value to.
649                 launchIntent.putExtra(EXTRA_SHARED_PREF, mSharedPrefResourceString);
650 
651                 Activity activity = (Activity) view.getContext();
652                 activity.startActivityForResult(
653                         launchIntent,
654                         AnalogComplicationConfigActivity.UPDATE_COLORS_CONFIG_REQUEST_CODE);
655             }
656         }
657     }
658 
659     /**
660      * Displays switch to indicate whether or not icon appears for unread notifications. User can
661      * toggle on/off.
662      */
663     public class UnreadNotificationViewHolder extends RecyclerView.ViewHolder
664             implements OnClickListener {
665 
666         private Switch mUnreadNotificationSwitch;
667 
668         private int mEnabledIconResourceId;
669         private int mDisabledIconResourceId;
670 
671         private int mSharedPrefResourceId;
672 
UnreadNotificationViewHolder(View view)673         public UnreadNotificationViewHolder(View view) {
674             super(view);
675 
676             mUnreadNotificationSwitch = (Switch) view.findViewById(R.id.unread_notification_switch);
677             view.setOnClickListener(this);
678         }
679 
setName(String name)680         public void setName(String name) {
681             mUnreadNotificationSwitch.setText(name);
682         }
683 
setIcons(int enabledIconResourceId, int disabledIconResourceId)684         public void setIcons(int enabledIconResourceId, int disabledIconResourceId) {
685 
686             mEnabledIconResourceId = enabledIconResourceId;
687             mDisabledIconResourceId = disabledIconResourceId;
688 
689             Context context = mUnreadNotificationSwitch.getContext();
690 
691             // Set default to enabled.
692             mUnreadNotificationSwitch.setCompoundDrawablesWithIntrinsicBounds(
693                     context.getDrawable(mEnabledIconResourceId), null, null, null);
694         }
695 
setSharedPrefId(int sharedPrefId)696         public void setSharedPrefId(int sharedPrefId) {
697             mSharedPrefResourceId = sharedPrefId;
698 
699             if (mUnreadNotificationSwitch != null) {
700 
701                 Context context = mUnreadNotificationSwitch.getContext();
702                 String sharedPreferenceString = context.getString(mSharedPrefResourceId);
703                 Boolean currentState = mSharedPref.getBoolean(sharedPreferenceString, true);
704 
705                 updateIcon(context, currentState);
706             }
707         }
708 
updateIcon(Context context, Boolean currentState)709         private void updateIcon(Context context, Boolean currentState) {
710             int currentIconResourceId;
711 
712             if (currentState) {
713                 currentIconResourceId = mEnabledIconResourceId;
714             } else {
715                 currentIconResourceId = mDisabledIconResourceId;
716             }
717 
718             mUnreadNotificationSwitch.setChecked(currentState);
719             mUnreadNotificationSwitch.setCompoundDrawablesWithIntrinsicBounds(
720                     context.getDrawable(currentIconResourceId), null, null, null);
721         }
722 
723         @Override
onClick(View view)724         public void onClick(View view) {
725             int position = getAdapterPosition();
726             Log.d(TAG, "Complication onClick() position: " + position);
727 
728             Context context = view.getContext();
729             String sharedPreferenceString = context.getString(mSharedPrefResourceId);
730 
731             // Since user clicked on a switch, new state should be opposite of current state.
732             Boolean newState = !mSharedPref.getBoolean(sharedPreferenceString, true);
733 
734             SharedPreferences.Editor editor = mSharedPref.edit();
735             editor.putBoolean(sharedPreferenceString, newState);
736             editor.apply();
737 
738             updateIcon(context, newState);
739         }
740     }
741 
742     /** Displays button to trigger background image complication selector. */
743     public class BackgroundComplicationViewHolder extends RecyclerView.ViewHolder
744             implements OnClickListener {
745 
746         private Button mBackgroundComplicationButton;
747 
BackgroundComplicationViewHolder(View view)748         public BackgroundComplicationViewHolder(View view) {
749             super(view);
750 
751             mBackgroundComplicationButton =
752                     (Button) view.findViewById(R.id.background_complication_button);
753             view.setOnClickListener(this);
754         }
755 
setName(String name)756         public void setName(String name) {
757             mBackgroundComplicationButton.setText(name);
758         }
759 
setIcon(int resourceId)760         public void setIcon(int resourceId) {
761             Context context = mBackgroundComplicationButton.getContext();
762             mBackgroundComplicationButton.setCompoundDrawablesWithIntrinsicBounds(
763                     context.getDrawable(resourceId), null, null, null);
764         }
765 
766         @Override
onClick(View view)767         public void onClick(View view) {
768             int position = getAdapterPosition();
769             Log.d(TAG, "Background Complication onClick() position: " + position);
770 
771             Activity currentActivity = (Activity) view.getContext();
772 
773             mSelectedComplicationId =
774                     AnalogComplicationWatchFaceService.getComplicationId(
775                             ComplicationLocation.BACKGROUND);
776 
777             if (mSelectedComplicationId >= 0) {
778 
779                 int[] supportedTypes =
780                         AnalogComplicationWatchFaceService.getSupportedComplicationTypes(
781                                 ComplicationLocation.BACKGROUND);
782 
783                 ComponentName watchFace =
784                         new ComponentName(
785                                 currentActivity, AnalogComplicationWatchFaceService.class);
786 
787                 currentActivity.startActivityForResult(
788                         ComplicationHelperActivity.createProviderChooserHelperIntent(
789                                 currentActivity,
790                                 watchFace,
791                                 mSelectedComplicationId,
792                                 supportedTypes),
793                         AnalogComplicationConfigActivity.COMPLICATION_CONFIG_REQUEST_CODE);
794 
795             } else {
796                 Log.d(TAG, "Complication not supported by watch face.");
797             }
798         }
799     }
800 }
801