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