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                 if (complicationProviderInfo != null) {
    522                     mLeftComplication.setImageIcon(complicationProviderInfo.providerIcon);
    523                     mLeftComplicationBackground.setVisibility(View.VISIBLE);
    524 
    525                 } else {
    526                     mLeftComplication.setImageDrawable(mDefaultComplicationDrawable);
    527                     mLeftComplicationBackground.setVisibility(View.INVISIBLE);
    528                 }
    529 
    530             } else if (watchFaceComplicationId == mRightComplicationId) {
    531                 if (complicationProviderInfo != null) {
    532                     mRightComplication.setImageIcon(complicationProviderInfo.providerIcon);
    533                     mRightComplicationBackground.setVisibility(View.VISIBLE);
    534 
    535                 } else {
    536                     mRightComplication.setImageDrawable(mDefaultComplicationDrawable);
    537                     mRightComplicationBackground.setVisibility(View.INVISIBLE);
    538                 }
    539             }
    540         }
    541 
    542         public void initializesColorsAndComplications() {
    543 
    544             // Initializes highlight color (just second arm and part of complications).
    545             String highlightSharedPrefString = mContext.getString(R.string.saved_marker_color);
    546             int currentHighlightColor = mSharedPref.getInt(highlightSharedPrefString, Color.RED);
    547 
    548             PorterDuffColorFilter highlightColorFilter =
    549                     new PorterDuffColorFilter(currentHighlightColor, PorterDuff.Mode.SRC_ATOP);
    550 
    551             mWatchFaceHighlightPreviewView.getBackground().setColorFilter(highlightColorFilter);
    552 
    553             // Initializes background color to gray (updates to color or complication icon based
    554             // on whether the background complication is live or not.
    555             PorterDuffColorFilter backgroundColorFilter =
    556                     new PorterDuffColorFilter(Color.GRAY, PorterDuff.Mode.SRC_ATOP);
    557 
    558             mWatchFaceBackgroundPreviewImageView
    559                     .getBackground()
    560                     .setColorFilter(backgroundColorFilter);
    561 
    562             final int[] complicationIds = AnalogComplicationWatchFaceService.getComplicationIds();
    563 
    564             mProviderInfoRetriever.retrieveProviderInfo(
    565                     new OnProviderInfoReceivedCallback() {
    566                         @Override
    567                         public void onProviderInfoReceived(
    568                                 int watchFaceComplicationId,
    569                                 @Nullable ComplicationProviderInfo complicationProviderInfo) {
    570 
    571                             Log.d(TAG, "onProviderInfoReceived: " + complicationProviderInfo);
    572 
    573                             updateComplicationViews(
    574                                     watchFaceComplicationId, complicationProviderInfo);
    575                         }
    576                     },
    577                     mWatchFaceComponentName,
    578                     complicationIds);
    579         }
    580     }
    581 
    582     /** Displays icon to indicate there are more options below the fold. */
    583     public class MoreOptionsViewHolder extends RecyclerView.ViewHolder {
    584 
    585         private ImageView mMoreOptionsImageView;
    586 
    587         public MoreOptionsViewHolder(View view) {
    588             super(view);
    589             mMoreOptionsImageView = (ImageView) view.findViewById(R.id.more_options_image_view);
    590         }
    591 
    592         public void setIcon(int resourceId) {
    593             Context context = mMoreOptionsImageView.getContext();
    594             mMoreOptionsImageView.setImageDrawable(context.getDrawable(resourceId));
    595         }
    596     }
    597 
    598     /**
    599      * Displays color options for the an item on the watch face. These could include marker color,
    600      * background color, etc.
    601      */
    602     public class ColorPickerViewHolder extends RecyclerView.ViewHolder implements OnClickListener {
    603 
    604         private Button mAppearanceButton;
    605 
    606         private String mSharedPrefResourceString;
    607 
    608         private Class<ColorSelectionActivity> mLaunchActivityToSelectColor;
    609 
    610         public ColorPickerViewHolder(View view) {
    611             super(view);
    612 
    613             mAppearanceButton = (Button) view.findViewById(R.id.color_picker_button);
    614             view.setOnClickListener(this);
    615         }
    616 
    617         public void setName(String name) {
    618             mAppearanceButton.setText(name);
    619         }
    620 
    621         public void setIcon(int resourceId) {
    622             Context context = mAppearanceButton.getContext();
    623             mAppearanceButton.setCompoundDrawablesWithIntrinsicBounds(
    624                     context.getDrawable(resourceId), null, null, null);
    625         }
    626 
    627         public void setSharedPrefString(String sharedPrefString) {
    628             mSharedPrefResourceString = sharedPrefString;
    629         }
    630 
    631         public void setLaunchActivityToSelectColor(Class<ColorSelectionActivity> activity) {
    632             mLaunchActivityToSelectColor = activity;
    633         }
    634 
    635         @Override
    636         public void onClick(View view) {
    637             int position = getAdapterPosition();
    638             Log.d(TAG, "Complication onClick() position: " + position);
    639 
    640             if (mLaunchActivityToSelectColor != null) {
    641                 Intent launchIntent = new Intent(view.getContext(), mLaunchActivityToSelectColor);
    642 
    643                 // Pass shared preference name to save color value to.
    644                 launchIntent.putExtra(EXTRA_SHARED_PREF, mSharedPrefResourceString);
    645 
    646                 Activity activity = (Activity) view.getContext();
    647                 activity.startActivityForResult(
    648                         launchIntent,
    649                         AnalogComplicationConfigActivity.UPDATE_COLORS_CONFIG_REQUEST_CODE);
    650             }
    651         }
    652     }
    653 
    654     /**
    655      * Displays switch to indicate whether or not icon appears for unread notifications. User can
    656      * toggle on/off.
    657      */
    658     public class UnreadNotificationViewHolder extends RecyclerView.ViewHolder
    659             implements OnClickListener {
    660 
    661         private Switch mUnreadNotificationSwitch;
    662 
    663         private int mEnabledIconResourceId;
    664         private int mDisabledIconResourceId;
    665 
    666         private int mSharedPrefResourceId;
    667 
    668         public UnreadNotificationViewHolder(View view) {
    669             super(view);
    670 
    671             mUnreadNotificationSwitch = (Switch) view.findViewById(R.id.unread_notification_switch);
    672             view.setOnClickListener(this);
    673         }
    674 
    675         public void setName(String name) {
    676             mUnreadNotificationSwitch.setText(name);
    677         }
    678 
    679         public void setIcons(int enabledIconResourceId, int disabledIconResourceId) {
    680 
    681             mEnabledIconResourceId = enabledIconResourceId;
    682             mDisabledIconResourceId = disabledIconResourceId;
    683 
    684             Context context = mUnreadNotificationSwitch.getContext();
    685 
    686             // Set default to enabled.
    687             mUnreadNotificationSwitch.setCompoundDrawablesWithIntrinsicBounds(
    688                     context.getDrawable(mEnabledIconResourceId), null, null, null);
    689         }
    690 
    691         public void setSharedPrefId(int sharedPrefId) {
    692             mSharedPrefResourceId = sharedPrefId;
    693 
    694             if (mUnreadNotificationSwitch != null) {
    695 
    696                 Context context = mUnreadNotificationSwitch.getContext();
    697                 String sharedPreferenceString = context.getString(mSharedPrefResourceId);
    698                 Boolean currentState = mSharedPref.getBoolean(sharedPreferenceString, true);
    699 
    700                 updateIcon(context, currentState);
    701             }
    702         }
    703 
    704         private void updateIcon(Context context, Boolean currentState) {
    705             int currentIconResourceId;
    706 
    707             if (currentState) {
    708                 currentIconResourceId = mEnabledIconResourceId;
    709             } else {
    710                 currentIconResourceId = mDisabledIconResourceId;
    711             }
    712 
    713             mUnreadNotificationSwitch.setChecked(currentState);
    714             mUnreadNotificationSwitch.setCompoundDrawablesWithIntrinsicBounds(
    715                     context.getDrawable(currentIconResourceId), null, null, null);
    716         }
    717 
    718         @Override
    719         public void onClick(View view) {
    720             int position = getAdapterPosition();
    721             Log.d(TAG, "Complication onClick() position: " + position);
    722 
    723             Context context = view.getContext();
    724             String sharedPreferenceString = context.getString(mSharedPrefResourceId);
    725 
    726             // Since user clicked on a switch, new state should be opposite of current state.
    727             Boolean newState = !mSharedPref.getBoolean(sharedPreferenceString, true);
    728 
    729             SharedPreferences.Editor editor = mSharedPref.edit();
    730             editor.putBoolean(sharedPreferenceString, newState);
    731             editor.apply();
    732 
    733             updateIcon(context, newState);
    734         }
    735     }
    736 
    737     /** Displays button to trigger background image complication selector. */
    738     public class BackgroundComplicationViewHolder extends RecyclerView.ViewHolder
    739             implements OnClickListener {
    740 
    741         private Button mBackgroundComplicationButton;
    742 
    743         public BackgroundComplicationViewHolder(View view) {
    744             super(view);
    745 
    746             mBackgroundComplicationButton =
    747                     (Button) view.findViewById(R.id.background_complication_button);
    748             view.setOnClickListener(this);
    749         }
    750 
    751         public void setName(String name) {
    752             mBackgroundComplicationButton.setText(name);
    753         }
    754 
    755         public void setIcon(int resourceId) {
    756             Context context = mBackgroundComplicationButton.getContext();
    757             mBackgroundComplicationButton.setCompoundDrawablesWithIntrinsicBounds(
    758                     context.getDrawable(resourceId), null, null, null);
    759         }
    760 
    761         @Override
    762         public void onClick(View view) {
    763             int position = getAdapterPosition();
    764             Log.d(TAG, "Background Complication onClick() position: " + position);
    765 
    766             Activity currentActivity = (Activity) view.getContext();
    767 
    768             mSelectedComplicationId =
    769                     AnalogComplicationWatchFaceService.getComplicationId(
    770                             ComplicationLocation.BACKGROUND);
    771 
    772             if (mSelectedComplicationId >= 0) {
    773 
    774                 int[] supportedTypes =
    775                         AnalogComplicationWatchFaceService.getSupportedComplicationTypes(
    776                                 ComplicationLocation.BACKGROUND);
    777 
    778                 ComponentName watchFace =
    779                         new ComponentName(
    780                                 currentActivity, AnalogComplicationWatchFaceService.class);
    781 
    782                 currentActivity.startActivityForResult(
    783                         ComplicationHelperActivity.createProviderChooserHelperIntent(
    784                                 currentActivity,
    785                                 watchFace,
    786                                 mSelectedComplicationId,
    787                                 supportedTypes),
    788                         AnalogComplicationConfigActivity.COMPLICATION_CONFIG_REQUEST_CODE);
    789 
    790             } else {
    791                 Log.d(TAG, "Complication not supported by watch face.");
    792             }
    793         }
    794     }
    795 }
    796