1 /*
2 * Copyright 2013 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 
18 package com.example.android.batchstepsensor.cardstream;
19 
20 import android.animation.Animator;
21 import android.animation.AnimatorListenerAdapter;
22 import android.animation.ObjectAnimator;
23 import android.app.Activity;
24 import android.graphics.Color;
25 import android.view.LayoutInflater;
26 import android.view.View;
27 import android.view.ViewGroup;
28 import android.widget.Button;
29 import android.widget.ProgressBar;
30 import android.widget.TextView;
31 
32 import com.example.android.batchstepsensor.R;
33 
34 import java.util.ArrayList;
35 
36 /**
37  * A Card contains a description and has a visual state. Optionally a card also contains a title,
38  * progress indicator and zero or more actions. It is constructed through the {@link Builder}.
39  */
40 public class Card {
41 
42     public static final int ACTION_POSITIVE = 1;
43     public static final int ACTION_NEGATIVE = 2;
44     public static final int ACTION_NEUTRAL = 3;
45 
46     public static final int PROGRESS_TYPE_NO_PROGRESS = 0;
47     public static final int PROGRESS_TYPE_NORMAL = 1;
48     public static final int PROGRESS_TYPE_INDETERMINATE = 2;
49     public static final int PROGRESS_TYPE_LABEL = 3;
50 
51     private OnCardClickListener mClickListener;
52 
53 
54     // The card model contains a reference to its desired layout (for extensibility), title,
55     // description, zero to many action buttons, and zero or 1 progress indicators.
56     private int mLayoutId = R.layout.card;
57 
58     /**
59      * Tag that uniquely identifies this card.
60      */
61     private String mTag = null;
62 
63     private String mTitle = null;
64     private String mDescription = null;
65 
66     private View mCardView = null;
67     private View mOverlayView = null;
68     private TextView mTitleView = null;
69     private TextView mDescView = null;
70     private View mActionAreaView = null;
71 
72     private Animator mOngoingAnimator = null;
73 
74     /**
75      * Visual state, either {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED} or
76      * {@link #CARD_STATE_INACTIVE}.
77      */
78     private int mCardState = CARD_STATE_NORMAL;
79     public static final int CARD_STATE_NORMAL = 1;
80     public static final int CARD_STATE_FOCUSED = 2;
81     public static final int CARD_STATE_INACTIVE = 3;
82 
83     /**
84      * Represent actions that can be taken from the card.  Stylistically the developer can
85      * designate the action as positive, negative (ok/cancel, for instance), or neutral.
86      * This "type" can be used as a UI hint.
87      * @see com.example.android.sensors.batchstepsensor.Card.CardAction
88      */
89     private ArrayList<CardAction> mCardActions = new ArrayList<CardAction>();
90 
91     /**
92      * Some cards will have a sense of "progress" which should be associated with, but separated
93      * from its "parent" card.  To push for simplicity in samples, Cards are designed to have
94      * a maximum of one progress indicator per Card.
95      */
96     private CardProgress mCardProgress = null;
97 
Card()98     public Card() {
99     }
100 
getTag()101     public String getTag() {
102         return mTag;
103     }
104 
getView()105     public View getView() {
106         return mCardView;
107     }
108 
109 
setDescription(String desc)110     public Card setDescription(String desc) {
111         if (mDescView != null) {
112             mDescription = desc;
113             mDescView.setText(desc);
114         }
115         return this;
116     }
117 
setTitle(String title)118     public Card setTitle(String title) {
119         if (mTitleView != null) {
120             mTitle = title;
121             mTitleView.setText(title);
122         }
123         return this;
124     }
125 
126 
127     /**
128      * Return the UI state, either {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED}
129      * or {@link #CARD_STATE_INACTIVE}.
130      */
getState()131     public int getState() {
132         return mCardState;
133     }
134 
135     /**
136      * Set the UI state. The parameter describes the state and must be either
137      * {@link #CARD_STATE_NORMAL}, {@link #CARD_STATE_FOCUSED} or {@link #CARD_STATE_INACTIVE}.
138      * Note: This method must be called from the UI Thread.
139      * @param state
140      * @return The card itself, allows for chaining of calls
141      */
setState(int state)142     public Card setState(int state) {
143         mCardState = state;
144         if (null != mOverlayView) {
145             if (null != mOngoingAnimator) {
146                 mOngoingAnimator.end();
147                 mOngoingAnimator = null;
148             }
149             switch (state) {
150                 case CARD_STATE_NORMAL: {
151                     mOverlayView.setVisibility(View.GONE);
152                     mOverlayView.setAlpha(1.f);
153                     break;
154                 }
155                 case CARD_STATE_FOCUSED: {
156                     mOverlayView.setVisibility(View.VISIBLE);
157                     mOverlayView.setBackgroundResource(R.drawable.card_overlay_focused);
158                     ObjectAnimator animator = ObjectAnimator.ofFloat(mOverlayView, "alpha", 0.f);
159                     animator.setRepeatMode(ObjectAnimator.REVERSE);
160                     animator.setRepeatCount(ObjectAnimator.INFINITE);
161                     animator.setDuration(1000);
162                     animator.start();
163                     mOngoingAnimator = animator;
164                     break;
165                 }
166                 case CARD_STATE_INACTIVE: {
167                     mOverlayView.setVisibility(View.VISIBLE);
168                     mOverlayView.setAlpha(1.f);
169                     mOverlayView.setBackgroundColor(Color.argb(0xaa, 0xcc, 0xcc, 0xcc));
170                     break;
171                 }
172             }
173         }
174         return this;
175     }
176 
177     /**
178      * Set the type of progress indicator.
179      * The progress type can only be changed if the Card was initially build with a progress
180      * indicator.
181      * See {@link Builder#setProgressType(int)}.
182      * Must be a value of either {@link #PROGRESS_TYPE_NORMAL},
183      * {@link #PROGRESS_TYPE_INDETERMINATE}, {@link #PROGRESS_TYPE_LABEL} or
184      * {@link #PROGRESS_TYPE_NO_PROGRESS}.
185      * @param progressType
186      * @return The card itself, allows for chaining of calls
187      */
setProgressType(int progressType)188     public Card setProgressType(int progressType) {
189         if (mCardProgress == null) {
190             mCardProgress = new CardProgress();
191         }
192         mCardProgress.setProgressType(progressType);
193         return this;
194     }
195 
196     /**
197      * Return the progress indicator type. A value of either {@link #PROGRESS_TYPE_NORMAL},
198      * {@link #PROGRESS_TYPE_INDETERMINATE}, {@link #PROGRESS_TYPE_LABEL}. Otherwise if no progress
199      * indicator is enabled, {@link #PROGRESS_TYPE_NO_PROGRESS} is returned.
200      * @return
201      */
getProgressType()202     public int getProgressType() {
203         if (mCardProgress == null) {
204             return PROGRESS_TYPE_NO_PROGRESS;
205         }
206         return mCardProgress.progressType;
207     }
208 
209     /**
210      * Set the progress to the specified value. Only applicable if the card has a
211      * {@link #PROGRESS_TYPE_NORMAL} progress type.
212      * @param progress
213      * @return
214      * @see #setMaxProgress(int)
215      */
setProgress(int progress)216     public Card setProgress(int progress) {
217         if (mCardProgress != null) {
218             mCardProgress.setProgress(progress);
219         }
220         return this;
221     }
222 
223     /**
224      * Set the range of the progress to 0...max. Only applicable if the card has a
225      * {@link #PROGRESS_TYPE_NORMAL} progress type.
226      * @return
227      */
setMaxProgress(int max)228     public Card setMaxProgress(int max){
229         if (mCardProgress != null) {
230             mCardProgress.setMax(max);
231         }
232         return this;
233     }
234 
235     /**
236      * Set the label text for the progress if the card has a progress type of
237      * {@link #PROGRESS_TYPE_NORMAL}, {@link #PROGRESS_TYPE_INDETERMINATE} or
238      * {@link #PROGRESS_TYPE_LABEL}
239      * @param text
240      * @return
241      */
setProgressLabel(String text)242     public Card setProgressLabel(String text) {
243         if (mCardProgress != null) {
244             mCardProgress.setProgressLabel(text);
245         }
246         return this;
247     }
248 
249     /**
250      * Toggle the visibility of the progress section of the card. Only applicable if
251      * the card has a progress type of
252      * {@link #PROGRESS_TYPE_NORMAL}, {@link #PROGRESS_TYPE_INDETERMINATE} or
253      * {@link #PROGRESS_TYPE_LABEL}.
254      * @param isVisible
255      * @return
256      */
setProgressVisibility(boolean isVisible)257     public Card setProgressVisibility(boolean isVisible) {
258         if (mCardProgress.progressView == null) {
259             return this; // Card does not have progress
260         }
261         mCardProgress.progressView.setVisibility(isVisible ? View.VISIBLE : View.GONE);
262 
263         return this;
264     }
265 
266     /**
267      * Adds an action to this card during build time.
268      *
269      * @param label
270      * @param id
271      * @param type
272      */
addAction(String label, int id, int type)273     private void addAction(String label, int id, int type) {
274         CardAction cardAction = new CardAction();
275         cardAction.label = label;
276         cardAction.id = id;
277         cardAction.type = type;
278         mCardActions.add(cardAction);
279     }
280 
281     /**
282      * Toggles the visibility of a card action.
283      * @param actionId
284      * @param isVisible
285      * @return
286      */
setActionVisibility(int actionId, boolean isVisible)287     public Card setActionVisibility(int actionId, boolean isVisible) {
288         int visibilityFlag = isVisible ? View.VISIBLE : View.GONE;
289         for (CardAction action : mCardActions) {
290             if (action.id == actionId && action.actionView != null) {
291                 action.actionView.setVisibility(visibilityFlag);
292             }
293         }
294         return this;
295     }
296 
297     /**
298      * Toggles visibility of the action area of this Card through an animation.
299      * @param isVisible
300      * @return
301      */
setActionAreaVisibility(boolean isVisible)302     public Card setActionAreaVisibility(boolean isVisible) {
303         if (mActionAreaView == null) {
304             return this; // Card does not have an action area
305         }
306 
307         if (isVisible) {
308             // Show the action area
309             mActionAreaView.setVisibility(View.VISIBLE);
310             mActionAreaView.setPivotY(0.f);
311             mActionAreaView.setPivotX(mCardView.getWidth() / 2.f);
312             mActionAreaView.setAlpha(0.5f);
313             mActionAreaView.setRotationX(-90.f);
314             mActionAreaView.animate().rotationX(0.f).alpha(1.f).setDuration(400);
315         } else {
316             // Hide the action area
317             mActionAreaView.setPivotY(0.f);
318             mActionAreaView.setPivotX(mCardView.getWidth() / 2.f);
319             mActionAreaView.animate().rotationX(-90.f).alpha(0.f).setDuration(400).setListener(
320                     new AnimatorListenerAdapter() {
321                         @Override
322                         public void onAnimationEnd(Animator animation) {
323                             mActionAreaView.setVisibility(View.GONE);
324                         }
325                     });
326         }
327         return this;
328     }
329 
330 
331     /**
332      * Creates a shallow clone of the card.  Shallow means all values are present, but no views.
333      * This is useful for saving/restoring in the case of configuration changes, like screen
334      * rotation.
335      *
336      * @return A shallow clone of the card instance
337      */
createShallowClone()338     public Card createShallowClone() {
339         Card cloneCard = new Card();
340 
341         // Outer card values
342         cloneCard.mTitle = mTitle;
343         cloneCard.mDescription = mDescription;
344         cloneCard.mTag = mTag;
345         cloneCard.mLayoutId = mLayoutId;
346         cloneCard.mCardState = mCardState;
347 
348         // Progress
349         if (mCardProgress != null) {
350             cloneCard.mCardProgress = mCardProgress.createShallowClone();
351         }
352 
353         // Actions
354         for (CardAction action : mCardActions) {
355             cloneCard.mCardActions.add(action.createShallowClone());
356         }
357 
358         return cloneCard;
359     }
360 
361 
362     /**
363      * Prepare the card to be stored for configuration change.
364      */
prepareForConfigurationChange()365     public void prepareForConfigurationChange() {
366         // Null out views.
367         mCardView = null;
368         for (CardAction action : mCardActions) {
369             action.actionView = null;
370         }
371         mCardProgress.progressView = null;
372     }
373 
374     /**
375      * Creates a new {@link #Card}.
376      */
377     public static class Builder {
378         private Card mCard;
379 
380         /**
381          * Instantiate the builder with data from a shallow clone.
382          * @param listener
383          * @param card
384          * @see Card#createShallowClone()
385          */
Builder(OnCardClickListener listener, Card card)386         protected Builder(OnCardClickListener listener, Card card) {
387             mCard = card;
388             mCard.mClickListener = listener;
389         }
390 
391         /**
392          * Instantiate the builder with the tag of the card.
393          * @param listener
394          * @param tag
395          */
Builder(OnCardClickListener listener, String tag)396         public Builder(OnCardClickListener listener, String tag) {
397             mCard = new Card();
398             mCard.mTag = tag;
399             mCard.mClickListener = listener;
400         }
401 
setTitle(String title)402         public Builder setTitle(String title) {
403             mCard.mTitle = title;
404             return this;
405         }
406 
setDescription(String desc)407         public Builder setDescription(String desc) {
408             mCard.mDescription = desc;
409             return this;
410         }
411 
412         /**
413          * Add an action.
414          * The type describes how this action will be displayed. Accepted values are
415          * {@link #ACTION_NEUTRAL}, {@link #ACTION_POSITIVE} or {@link #ACTION_NEGATIVE}.
416          *
417          * @param label The text to display for this action
418          * @param id Identifier for this action, supplied in the click listener
419          * @param type UI style of action
420          * @return
421          */
addAction(String label, int id, int type)422         public Builder addAction(String label, int id, int type) {
423             mCard.addAction(label, id, type);
424             return this;
425         }
426 
427         /**
428          * Override the default layout.
429          * The referenced layout file has to contain the same identifiers as defined in the default
430          * layout configuration.
431          * @param layout
432          * @return
433          * @see R.layout.card
434          */
setLayout(int layout)435         public Builder setLayout(int layout) {
436             mCard.mLayoutId = layout;
437             return this;
438         }
439 
440         /**
441          * Set the type of progress bar to display.
442          * Accepted values are:
443          * <ul>
444          *     <li>{@link #PROGRESS_TYPE_NO_PROGRESS} disables the progress indicator</li>
445          *     <li>{@link #PROGRESS_TYPE_NORMAL}
446          *     displays a standard, linear progress indicator.</li>
447          *     <li>{@link #PROGRESS_TYPE_INDETERMINATE} displays an indeterminate (infite) progress
448          *     indicator.</li>
449          *     <li>{@link #PROGRESS_TYPE_LABEL} only displays a label text in the progress area
450          *     of the card.</li>
451          * </ul>
452          *
453          * @param progressType
454          * @return
455          */
setProgressType(int progressType)456         public Builder setProgressType(int progressType) {
457             mCard.setProgressType(progressType);
458             return this;
459         }
460 
setProgressLabel(String label)461         public Builder setProgressLabel(String label) {
462             // ensure the progress layout has been initialized, use 'no progress' by default
463             if (mCard.mCardProgress == null) {
464                 mCard.setProgressType(PROGRESS_TYPE_NO_PROGRESS);
465             }
466             mCard.mCardProgress.label = label;
467             return this;
468         }
469 
setProgressMaxValue(int maxValue)470         public Builder setProgressMaxValue(int maxValue) {
471             // ensure the progress layout has been initialized, use 'no progress' by default
472             if (mCard.mCardProgress == null) {
473                 mCard.setProgressType(PROGRESS_TYPE_NO_PROGRESS);
474             }
475             mCard.mCardProgress.maxValue = maxValue;
476             return this;
477         }
478 
setStatus(int status)479         public Builder setStatus(int status) {
480             mCard.setState(status);
481             return this;
482         }
483 
build(Activity activity)484         public Card build(Activity activity) {
485             LayoutInflater inflater = activity.getLayoutInflater();
486             // Inflating the card.
487             ViewGroup cardView = (ViewGroup) inflater.inflate(mCard.mLayoutId,
488                     (ViewGroup) activity.findViewById(R.id.card_stream), false);
489 
490             // Check that the layout contains a TextView with the card_title id
491             View viewTitle = cardView.findViewById(R.id.card_title);
492             if (mCard.mTitle != null && viewTitle != null) {
493                 mCard.mTitleView = (TextView) viewTitle;
494                 mCard.mTitleView.setText(mCard.mTitle);
495             } else if (viewTitle != null) {
496                 viewTitle.setVisibility(View.GONE);
497             }
498 
499             // Check that the layout contains a TextView with the card_content id
500             View viewDesc = cardView.findViewById(R.id.card_content);
501             if (mCard.mDescription != null && viewDesc != null) {
502                 mCard.mDescView = (TextView) viewDesc;
503                 mCard.mDescView.setText(mCard.mDescription);
504             } else if (viewDesc != null) {
505                 cardView.findViewById(R.id.card_content).setVisibility(View.GONE);
506             }
507 
508 
509             ViewGroup actionArea = (ViewGroup) cardView.findViewById(R.id.card_actionarea);
510 
511             // Inflate Progress
512             initializeProgressView(inflater, actionArea);
513 
514             // Inflate all action views.
515             initializeActionViews(inflater, cardView, actionArea);
516 
517             mCard.mCardView = cardView;
518             mCard.mOverlayView = cardView.findViewById(R.id.card_overlay);
519 
520             return mCard;
521         }
522 
523         /**
524          * Initialize data from the given card.
525          * @param card
526          * @return
527          * @see Card#createShallowClone()
528          */
cloneFromCard(Card card)529         public Builder cloneFromCard(Card card) {
530             mCard = card.createShallowClone();
531             return this;
532         }
533 
534         /**
535          * Build the action views by inflating the appropriate layouts and setting the text and
536          * values.
537          * @param inflater
538          * @param cardView
539          * @param actionArea
540          */
initializeActionViews(LayoutInflater inflater, ViewGroup cardView, ViewGroup actionArea)541         private void initializeActionViews(LayoutInflater inflater, ViewGroup cardView,
542                                            ViewGroup actionArea) {
543             if (!mCard.mCardActions.isEmpty()) {
544                 // Set action area to visible only when actions are visible
545                 actionArea.setVisibility(View.VISIBLE);
546                 mCard.mActionAreaView = actionArea;
547             }
548 
549             // Inflate all card actions
550             for (final CardAction action : mCard.mCardActions) {
551 
552                 int useActionLayout = 0;
553                 switch (action.type) {
554                     case Card.ACTION_POSITIVE:
555                         useActionLayout = R.layout.card_button_positive;
556                         break;
557                     case Card.ACTION_NEGATIVE:
558                         useActionLayout = R.layout.card_button_negative;
559                         break;
560                     case Card.ACTION_NEUTRAL:
561                     default:
562                         useActionLayout = R.layout.card_button_neutral;
563                         break;
564                 }
565 
566                 action.actionView = inflater.inflate(useActionLayout, actionArea, false);
567                 Button actionButton = (Button) action.actionView.findViewById(R.id.card_button);
568 
569                 actionButton.setText(action.label);
570                 actionButton.setOnClickListener(new View.OnClickListener() {
571                     @Override
572                     public void onClick(View v) {
573                         mCard.mClickListener.onCardClick(action.id, mCard.mTag);
574                     }
575                 });
576                 actionArea.addView(action.actionView);
577             }
578         }
579 
580         /**
581          * Build the progress view into the given ViewGroup.
582          *
583          * @param inflater
584          * @param actionArea
585          */
initializeProgressView(LayoutInflater inflater, ViewGroup actionArea)586         private void initializeProgressView(LayoutInflater inflater, ViewGroup actionArea) {
587 
588             // Only inflate progress layout if a progress type other than NO_PROGRESS was set.
589             if (mCard.mCardProgress != null) {
590                 //Setup progress card.
591                 View progressView = inflater.inflate(R.layout.card_progress, actionArea, false);
592                 ProgressBar progressBar =
593                         (ProgressBar) progressView.findViewById(R.id.card_progress);
594                 ((TextView) progressView.findViewById(R.id.card_progress_text))
595                         .setText(mCard.mCardProgress.label);
596                 progressBar.setMax(mCard.mCardProgress.maxValue);
597                 progressBar.setProgress(0);
598                 mCard.mCardProgress.progressView = progressView;
599                 mCard.mCardProgress.setProgressType(mCard.getProgressType());
600                 actionArea.addView(progressView);
601             }
602         }
603     }
604 
605     /**
606      * Represents a clickable action, accessible from the bottom of the card.
607      * Fields include the label, an ID to specify the action that was performed in the callback,
608      * an action type (positive, negative, neutral), and the callback.
609      */
610     public class CardAction {
611 
612         public String label;
613         public int id;
614         public int type;
615         public View actionView;
616 
createShallowClone()617         public CardAction createShallowClone() {
618             CardAction actionClone = new CardAction();
619             actionClone.label = label;
620             actionClone.id = id;
621             actionClone.type = type;
622             return actionClone;
623             // Not the view.  Never the view (don't want to hold view references for
624             // onConfigurationChange.
625         }
626 
627     }
628 
629     /**
630      * Describes the progress of a {@link Card}.
631      * Three types of progress are supported:
632      * <ul><li>{@link Card#PROGRESS_TYPE_NORMAL: Standard progress bar with label text</li>
633      * <li>{@link Card#PROGRESS_TYPE_INDETERMINATE}: Indeterminate progress bar with label txt</li>
634      * <li>{@link Card#PROGRESS_TYPE_LABEL}: Label only, no progresss bar</li>
635      * </ul>
636      */
637     public class CardProgress {
638         private int progressType = Card.PROGRESS_TYPE_NO_PROGRESS;
639         private String label = "";
640         private int currProgress = 0;
641         private int maxValue = 100;
642 
643         public View progressView = null;
644         private ProgressBar progressBar = null;
645         private TextView progressLabel = null;
646 
createShallowClone()647         public CardProgress createShallowClone() {
648             CardProgress progressClone = new CardProgress();
649             progressClone.label = label;
650             progressClone.currProgress = currProgress;
651             progressClone.maxValue = maxValue;
652             progressClone.progressType = progressType;
653             return progressClone;
654         }
655 
656         /**
657          * Set the progress. Only useful for the type {@link #PROGRESS_TYPE_NORMAL}.
658          * @param progress
659          * @see android.widget.ProgressBar#setProgress(int)
660          */
setProgress(int progress)661         public void setProgress(int progress) {
662             currProgress = progress;
663             final ProgressBar bar = getProgressBar();
664             if (bar != null) {
665                 bar.setProgress(currProgress);
666                 bar.invalidate();
667             }
668         }
669 
670         /**
671          * Set the range of the progress to 0...max.
672          * Only useful for the type {@link #PROGRESS_TYPE_NORMAL}.
673          * @param max
674          * @see android.widget.ProgressBar#setMax(int)
675          */
setMax(int max)676         public void setMax(int max) {
677             maxValue = max;
678             final ProgressBar bar = getProgressBar();
679             if (bar != null) {
680                 bar.setMax(maxValue);
681             }
682         }
683 
684         /**
685          * Set the label text that appears near the progress indicator.
686          * @param text
687          */
setProgressLabel(String text)688         public void setProgressLabel(String text) {
689             label = text;
690             final TextView labelView = getProgressLabel();
691             if (labelView != null) {
692                 labelView.setText(text);
693             }
694         }
695 
696         /**
697          * Set how progress is displayed. The parameter must be one of three supported types:
698          * <ul><li>{@link Card#PROGRESS_TYPE_NORMAL: Standard progress bar with label text</li>
699          * <li>{@link Card#PROGRESS_TYPE_INDETERMINATE}:
700          * Indeterminate progress bar with label txt</li>
701          * <li>{@link Card#PROGRESS_TYPE_LABEL}: Label only, no progresss bar</li>
702          * @param type
703          */
setProgressType(int type)704         public void setProgressType(int type) {
705             progressType = type;
706             if (progressView != null) {
707                 switch (type) {
708                     case PROGRESS_TYPE_NO_PROGRESS: {
709                         progressView.setVisibility(View.GONE);
710                         break;
711                     }
712                     case PROGRESS_TYPE_NORMAL: {
713                         progressView.setVisibility(View.VISIBLE);
714                         getProgressBar().setIndeterminate(false);
715                         break;
716                     }
717                     case PROGRESS_TYPE_INDETERMINATE: {
718                         progressView.setVisibility(View.VISIBLE);
719                         getProgressBar().setIndeterminate(true);
720                         break;
721                     }
722                 }
723             }
724         }
725 
getProgressLabel()726         private TextView getProgressLabel() {
727             if (progressLabel != null) {
728                 return progressLabel;
729             } else if (progressView != null) {
730                 progressLabel = (TextView) progressView.findViewById(R.id.card_progress_text);
731                 return progressLabel;
732             } else {
733                 return null;
734             }
735         }
736 
getProgressBar()737         private ProgressBar getProgressBar() {
738             if (progressBar != null) {
739                 return progressBar;
740             } else if (progressView != null) {
741                 progressBar = (ProgressBar) progressView.findViewById(R.id.card_progress);
742                 return progressBar;
743             } else {
744                 return null;
745             }
746         }
747 
748     }
749 }
750 
751