1 /*
2  * Copyright (C) 2014 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.leanback;
18 
19 import android.app.Activity;
20 import android.app.Fragment;
21 import android.app.FragmentManager;
22 import android.content.Context;
23 import android.content.res.Configuration;
24 import android.graphics.drawable.Drawable;
25 import android.os.Bundle;
26 import android.text.InputType;
27 import android.text.TextUtils;
28 import android.util.Log;
29 import android.view.LayoutInflater;
30 import android.view.View;
31 import android.view.ViewGroup;
32 import android.view.inputmethod.EditorInfo;
33 
34 import androidx.core.content.res.ResourcesCompat;
35 import androidx.leanback.app.GuidedStepFragment;
36 import androidx.leanback.widget.GuidanceStylist;
37 import androidx.leanback.widget.GuidanceStylist.Guidance;
38 import androidx.leanback.widget.GuidedAction;
39 import androidx.leanback.widget.GuidedActionsStylist;
40 import androidx.leanback.widget.GuidedDatePickerAction;
41 
42 import org.jspecify.annotations.NonNull;
43 import org.jspecify.annotations.Nullable;
44 
45 import java.util.ArrayList;
46 import java.util.Calendar;
47 import java.util.List;
48 
49 /**
50  * Activity that showcases different aspects of GuidedStepFragments.
51  */
52 public class GuidedStepActivity extends Activity {
53 
54     private static final int BACK = 2;
55 
56     private static final int FIRST_NAME = 3;
57     private static final int LAST_NAME = 4;
58     private static final int PASSWORD = 5;
59     private static final int PAYMENT = 6;
60     private static final int NEW_PAYMENT = 7;
61     private static final int PAYMENT_EXPIRE = 8;
62     private static final int REFRESH = 9;
63 
64     private static final long RADIO_ID_BASE = 0;
65     private static final long CHECKBOX_ID_BASE = 100;
66 
67     private static final long DEFAULT_OPTION = RADIO_ID_BASE;
68 
69     private static final String[] OPTION_NAMES = { "Option A", "Option B", "Option C" };
70     private static final String[] OPTION_DESCRIPTIONS = { "Here's one thing you can do",
71             "Here's another thing you can do", "Here's one more thing you can do" };
72 
73     private static final String TAG = GuidedStepActivity.class.getSimpleName();
74 
75     @Override
onCreate(@ullable Bundle savedInstanceState)76     protected void onCreate(@Nullable Bundle savedInstanceState) {
77         Log.v(TAG, "onCreate");
78         super.onCreate(savedInstanceState);
79         setContentView(R.layout.guided_step_activity);
80         if (savedInstanceState == null) {
81             GuidedStepFragment.addAsRoot(this, new FirstStepFragment(),
82                     R.id.lb_guidedstep_host);
83         }
84     }
85 
86     @Override
onConfigurationChanged(@ullable Configuration newConfig)87     public void onConfigurationChanged(@Nullable Configuration newConfig) {
88         Log.v(TAG, "onConfigurationChanged");
89         super.onConfigurationChanged(newConfig);
90     }
91 
92     @Override
onSaveInstanceState(@ullable Bundle outState)93     protected void onSaveInstanceState(@Nullable Bundle outState) {
94         Log.v(TAG, "onSaveInstanceState");
95         super.onSaveInstanceState(outState);
96     }
97 
98     @Override
onRestoreInstanceState(@ullable Bundle savedInstanceState)99     protected void onRestoreInstanceState(@Nullable Bundle savedInstanceState) {
100         Log.v(TAG, "onRestoreInstanceState");
101         super.onRestoreInstanceState(savedInstanceState);
102     }
103 
addAction(List<GuidedAction> actions, long id, String title, String desc)104     private static GuidedAction addAction(List<GuidedAction> actions, long id, String title,
105             String desc) {
106         GuidedAction action;
107         actions.add(action = new GuidedAction.Builder(null)
108                 .id(id)
109                 .title(title)
110                 .description(desc)
111                 .build());
112         return action;
113     }
114 
addAction(List<GuidedAction> actions, long id, String title, String desc, List<GuidedAction> subActions)115     private static GuidedAction addAction(List<GuidedAction> actions, long id, String title,
116             String desc, List<GuidedAction> subActions) {
117         GuidedAction action;
118         actions.add(action = new GuidedAction.Builder(null)
119                 .id(id)
120                 .title(title)
121                 .description(desc)
122                 .subActions(subActions)
123                 .build());
124         return action;
125     }
126 
addEditableAction(Context context, List<GuidedAction> actions, long id, String title, String desc)127     private static GuidedAction addEditableAction(Context context, List<GuidedAction> actions,
128             long id, String title, String desc) {
129         GuidedAction action;
130         actions.add(action = new GuidedAction.Builder(context)
131                 .id(id)
132                 .title(title)
133                 .description(desc)
134                 .editable(true)
135                 .icon(androidx.leanback.R.drawable.lb_ic_search_mic)
136                 .build());
137         return action;
138     }
139 
addEditableAction(List<GuidedAction> actions, long id, String title, String editTitle, int editInputType, String desc, String editDesc)140     private static GuidedAction addEditableAction(List<GuidedAction> actions, long id, String title,
141             String editTitle, int editInputType, String desc, String editDesc) {
142         GuidedAction action;
143         actions.add(action = new GuidedAction.Builder(null)
144                 .id(id)
145                 .title(title)
146                 .editTitle(editTitle)
147                 .editInputType(editInputType)
148                 .description(desc)
149                 .editDescription(editDesc)
150                 .editable(true)
151                 .build());
152         return action;
153     }
154 
addDatePickerAction(List<GuidedAction> actions, long id, String title)155     private static GuidedDatePickerAction addDatePickerAction(List<GuidedAction> actions, long id,
156             String title) {
157         GuidedDatePickerAction action;
158         actions.add(action = new GuidedDatePickerAction.Builder(null)
159                 .id(id)
160                 .title(title)
161                 .datePickerFormat("MY")
162                 .build());
163         return action;
164     }
165 
addEditableDescriptionAction(List<GuidedAction> actions, long id, String title, String desc, String editDescription, int descriptionEditInputType)166     private static GuidedAction addEditableDescriptionAction(List<GuidedAction> actions, long id,
167             String title, String desc, String editDescription, int descriptionEditInputType) {
168         GuidedAction action;
169         actions.add(action = new GuidedAction.Builder(null)
170                 .id(id)
171                 .title(title)
172                 .description(desc)
173                 .editDescription(editDescription)
174                 .descriptionEditInputType(descriptionEditInputType)
175                 .descriptionEditable(true)
176                 .build());
177         return action;
178     }
179 
addCheckedAction(List<GuidedAction> actions, long id, String title, String desc, int checkSetId)180     private static GuidedAction addCheckedAction(List<GuidedAction> actions, long id,
181             String title, String desc, int checkSetId) {
182         GuidedAction action;
183         actions.add(action = new GuidedAction.Builder(null)
184                 .id(id)
185                 .title(title)
186                 .description(desc)
187                 .checkSetId(checkSetId)
188                 .build());
189         return action;
190     }
191 
192     public static class FirstStepFragment extends GuidedStepFragment {
193 
194         @Override
onProvideTheme()195         public int onProvideTheme() {
196             return R.style.Theme_Example_Leanback_GuidedStep_First;
197         }
198 
199         @Override
onCreateGuidance(@ullable Bundle savedInstanceState)200         public @NonNull Guidance onCreateGuidance(@Nullable Bundle savedInstanceState) {
201             String title = getString(R.string.guidedstep_first_title);
202             String breadcrumb = getString(R.string.guidedstep_first_breadcrumb);
203             String description = getString(R.string.guidedstep_first_description);
204             final Context context = getActivity();
205             Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
206                     R.drawable.ic_main_icon, context.getTheme());
207             return new Guidance(title, description, breadcrumb, icon);
208         }
209 
210         @Override
onCreateActions(@onNull List<GuidedAction> actions, @Nullable Bundle savedInstanceState)211         public void onCreateActions(@NonNull List<GuidedAction> actions,
212                 @Nullable Bundle savedInstanceState) {
213             Context context = getActivity();
214             actions.add(new GuidedAction.Builder(context)
215                     .clickAction(GuidedAction.ACTION_ID_CONTINUE)
216                     .description("Let's do it")
217                     .build());
218             actions.add(new GuidedAction.Builder(context)
219                     .id(REFRESH)
220                     .title("Refresh")
221                     .build());
222             actions.add(new GuidedAction.Builder(context)
223                     .clickAction(GuidedAction.ACTION_ID_CANCEL)
224                     .description("Never mind")
225                     .build());
226         }
227 
228         @Override
onGuidedActionClicked(@onNull GuidedAction action)229         public void onGuidedActionClicked(@NonNull GuidedAction action) {
230             FragmentManager fm = getFragmentManager();
231             if (action.getId() == GuidedAction.ACTION_ID_CONTINUE) {
232                 GuidedStepFragment.add(fm, new SecondStepFragment(), R.id.lb_guidedstep_host);
233             } else if (action.getId() == REFRESH) {
234                 // swap actions position and change content:
235                 Context context = getActivity();
236                 ArrayList<GuidedAction> newActions = new ArrayList();
237                 newActions.add(new GuidedAction.Builder(context)
238                         .id(REFRESH)
239                         .title("Refresh done")
240                         .build());
241                 newActions.add(new GuidedAction.Builder(context)
242                         .clickAction(GuidedAction.ACTION_ID_CONTINUE)
243                         .description("Let's do it")
244                         .build());
245                 newActions.add(new GuidedAction.Builder(context)
246                         .clickAction(GuidedAction.ACTION_ID_CANCEL)
247                         .description("Never mind")
248                         .build());
249                 setActions(newActions);
250             } else if (action.getId() == GuidedAction.ACTION_ID_CANCEL){
251                 finishGuidedStepFragments();
252             }
253         }
254     }
255 
256     public interface NewPaymentFragmentTarget {
onNewPaymentFragmentStarted()257         void onNewPaymentFragmentStarted();
onNewPaymentAdded(int selection)258         void onNewPaymentAdded(int selection);
259     }
260 
261     static ArrayList<String> sCards = new ArrayList<String>();
262     static int sSelectedCard = -1;
263     static {
264         sCards.add("Visa-1234");
265         sCards.add("AmEx-4321");
266     }
267 
268     public static class NewPaymentStepFragment extends GuidedStepFragment {
269 
270         NewPaymentFragmentTarget mNewPaymentTarget;
271 
272         @Override
onCreate(@ullable Bundle savedInstance)273         public void onCreate(@Nullable Bundle savedInstance) {
274             super.onCreate(savedInstance);
275             Fragment targetFragment = getTargetFragment();
276             if (targetFragment instanceof NewPaymentFragmentTarget) {
277                 mNewPaymentTarget = ((NewPaymentFragmentTarget) targetFragment);
278                 mNewPaymentTarget.onNewPaymentFragmentStarted();
279             }
280         }
281 
282         @Override
onCreateGuidance(@ullable Bundle savedInstanceState)283         public @NonNull Guidance onCreateGuidance(@Nullable Bundle savedInstanceState) {
284             String title = getString(R.string.guidedstep_newpayment_title);
285             String breadcrumb = getString(R.string.guidedstep_newpayment_breadcrumb);
286             String description = getString(R.string.guidedstep_newpayment_description);
287             final Context context = getActivity();
288             Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
289                     R.drawable.ic_main_icon, context.getTheme());
290             return new Guidance(title, description, breadcrumb, icon);
291         }
292 
293         @Override
onCreateActions(@onNull List<GuidedAction> actions, @Nullable Bundle savedInstanceState)294         public void onCreateActions(@NonNull List<GuidedAction> actions,
295                 @Nullable Bundle savedInstanceState) {
296             addEditableAction(actions, NEW_PAYMENT, "Input credit card number", "",
297                     InputType.TYPE_CLASS_NUMBER,
298                     "Input credit card number", "Input credit card number");
299             addDatePickerAction(actions, PAYMENT_EXPIRE, "Exp:");
300         }
301 
302         @Override
onCreateButtonActions(@onNull List<GuidedAction> actions, @Nullable Bundle savedInstanceState)303         public void onCreateButtonActions(@NonNull List<GuidedAction> actions,
304                 @Nullable Bundle savedInstanceState) {
305             Context context = getActivity();
306             actions.add(new GuidedAction.Builder(context).clickAction(GuidedAction.ACTION_ID_OK)
307                     .build());
308             actions.get(actions.size() - 1).setEnabled(false);
309         }
310 
311         @Override
onGuidedActionClicked(@onNull GuidedAction action)312         public void onGuidedActionClicked(@NonNull GuidedAction action) {
313             if (action.getId() == GuidedAction.ACTION_ID_OK) {
314                 CharSequence desc = findActionById(NEW_PAYMENT).getDescription();
315                 String cardNumber = desc.subSequence(desc.length() - 4, desc.length()).toString();
316                 String card;
317                 if ((Integer.parseInt(cardNumber) & 1) == 0) {
318                     card = "Visa "+cardNumber;
319                 } else {
320                     card = "AmEx "+cardNumber;
321                 }
322                 int selection = sCards.size();
323                 sCards.add(card);
324                 if (mNewPaymentTarget != null) {
325                     mNewPaymentTarget.onNewPaymentAdded(selection);
326                 }
327                 popBackStackToGuidedStepFragment(NewPaymentStepFragment.class,
328                         FragmentManager.POP_BACK_STACK_INCLUSIVE);
329             }
330         }
331 
332         @Override
onGuidedActionEditedAndProceed(@onNull GuidedAction action)333         public long onGuidedActionEditedAndProceed(@NonNull GuidedAction action) {
334             if (action.getId() == NEW_PAYMENT) {
335                 CharSequence editTitle = action.getEditTitle();
336                 if (isCardNumberValid(editTitle)) {
337                     editTitle = editTitle.subSequence(editTitle.length() - 4, editTitle.length());
338                     action.setDescription("Visa XXXX-XXXX-XXXX-" + editTitle);
339                     updateOkButton(isExpDateValid(findActionById(PAYMENT_EXPIRE)));
340                     return GuidedAction.ACTION_ID_NEXT;
341                 } else if (editTitle.length() == 0) {
342                     action.setDescription("Input credit card number");
343                     updateOkButton(false);
344                     return GuidedAction.ACTION_ID_CURRENT;
345                 } else {
346                     action.setDescription("Error credit card number");
347                     updateOkButton(false);
348                     return GuidedAction.ACTION_ID_CURRENT;
349                 }
350             } else if (action.getId() == PAYMENT_EXPIRE) {
351                 updateOkButton(isExpDateValid(action) &&
352                         isCardNumberValid(findActionById(NEW_PAYMENT).getEditTitle()));
353             }
354             return GuidedAction.ACTION_ID_NEXT;
355         }
356 
isCardNumberValid(CharSequence number)357         boolean isCardNumberValid(CharSequence number) {
358             return TextUtils.isDigitsOnly(number) && number.length() == 16;
359         }
360 
isExpDateValid(GuidedAction action)361         boolean isExpDateValid(GuidedAction action) {
362             long date = ((GuidedDatePickerAction) action).getDate();
363             Calendar c = Calendar.getInstance();
364             c.setTimeInMillis(date);
365             return Calendar.getInstance().before(c);
366         }
367 
updateOkButton(boolean enabled)368         void updateOkButton(boolean enabled) {
369             findButtonActionById(GuidedAction.ACTION_ID_OK).setEnabled(enabled);
370             notifyButtonActionChanged(findButtonActionPositionById(GuidedAction.ACTION_ID_OK));
371         }
372     }
373 
374     public static class SecondStepFragment extends GuidedStepFragment
375             implements NewPaymentFragmentTarget {
376 
377 
378         boolean mExpandPaymentListInOnCreateView;
379 
380         @Override
onNewPaymentAdded(int selection)381         public void onNewPaymentAdded(int selection) {
382             // if a new payment is added, we don't need expand the sub actions list.
383             mExpandPaymentListInOnCreateView = false;
384             sSelectedCard = selection;
385             updatePaymentAction(findActionById(PAYMENT));
386             findButtonActionById(GuidedAction.ACTION_ID_CONTINUE).setEnabled(sSelectedCard != -1);
387         }
388 
389         @Override
onNewPaymentFragmentStarted()390         public void onNewPaymentFragmentStarted() {
391             // if a new payment fragment is opened, when come back we should expand the payment
392             // sub actions list unless user created a new payment in onNewPaymentAdded
393             mExpandPaymentListInOnCreateView = true;
394         }
395 
396         @Override
onCreateActionsStylist()397         public @NonNull GuidedActionsStylist onCreateActionsStylist() {
398             return new GuidedActionsStylist() {
399                 @Override
400                 protected void setupImeOptions(GuidedActionsStylist.ViewHolder vh,
401                         GuidedAction action) {
402                     if (action.getId() == PASSWORD) {
403                         vh.getEditableDescriptionView().setImeActionLabel("Confirm!",
404                                 EditorInfo.IME_ACTION_DONE);
405                     } else {
406                         super.setupImeOptions(vh, action);
407                     }
408                 }
409             };
410         }
411 
412         @Override
413         public @NonNull Guidance onCreateGuidance(@Nullable Bundle savedInstanceState) {
414             String title = getString(R.string.guidedstep_second_title);
415             String breadcrumb = getString(R.string.guidedstep_second_breadcrumb);
416             String description = getString(R.string.guidedstep_second_description);
417             final Context context = getActivity();
418             Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
419                     R.drawable.ic_main_icon, context.getTheme());
420             return new Guidance(title, description, breadcrumb, icon);
421         }
422 
423         @Override
424         public void onCreateActions(@NonNull List<GuidedAction> actions,
425                 @Nullable Bundle savedInstanceState) {
426             addEditableAction(getActivity(), actions, FIRST_NAME, "Pat", "Your first name");
427             addEditableAction(getActivity(), actions, LAST_NAME, "Smith", "Your last name");
428             List<GuidedAction> subActions = new ArrayList<GuidedAction>();
429             updatePaymentAction(addAction(actions, PAYMENT, "Select Payment", "", subActions));
430             addEditableDescriptionAction(actions, PASSWORD, "Password", "", "",
431                     InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
432         }
433 
434         @Override
435         public void onCreateButtonActions(@NonNull List<GuidedAction> actions,
436                 @Nullable Bundle savedInstanceState) {
437             actions.add(new GuidedAction.Builder(getActivity())
438                     .clickAction(GuidedAction.ACTION_ID_CONTINUE)
439                     .description("Continue")
440                     .enabled(isPasswordValid() && isPaymentValid())
441                     .build());
442         }
443 
444         @Override
445         public void onGuidedActionClicked(@NonNull GuidedAction action) {
446             if (action.getId() == GuidedAction.ACTION_ID_CONTINUE) {
447                 FragmentManager fm = getFragmentManager();
448                 GuidedStepFragment.add(fm, new ThirdStepFragment(), R.id.lb_guidedstep_host);
449             }
450         }
451 
452         void updatePaymentAction(GuidedAction paymentAction) {
453             List<GuidedAction> subActions = paymentAction.getSubActions();
454             subActions.clear();
455             for (int i = 0; i < sCards.size(); i++) {
456                 addCheckedAction(subActions, -1, sCards.get(i), "",
457                         GuidedAction.DEFAULT_CHECK_SET_ID);
458                 if (i == sSelectedCard) {
459                     subActions.get(i).setChecked(true);
460                 }
461             }
462             addAction(subActions, NEW_PAYMENT, "Add New Card", "");
463             paymentAction.setDescription(sSelectedCard == -1 ? "" : sCards.get(sSelectedCard));
464         }
465 
466         @Override
467         public long onGuidedActionEditedAndProceed(@NonNull GuidedAction action) {
468             if (action.getId() == PASSWORD) {
469                 CharSequence password = action.getEditDescription();
470                 if (password.length() > 0) {
471                     if (isPaymentValid()) {
472                         updateContinue(true);
473                         return GuidedAction.ACTION_ID_NEXT;
474                     } else {
475                         updateContinue(false);
476                         return GuidedAction.ACTION_ID_CURRENT;
477                     }
478                 } else {
479                     updateContinue(false);
480                     return GuidedAction.ACTION_ID_CURRENT;
481                 }
482             }
483             return GuidedAction.ACTION_ID_NEXT;
484         }
485 
486         @Override
487         public boolean onSubGuidedActionClicked(@NonNull GuidedAction action) {
488             if (action.isChecked()) {
489                 String payment = action.getTitle().toString();
490                 for (int i = 0; i < sCards.size(); i++) {
491                     if (payment.equals(sCards.get(i))) {
492                         sSelectedCard = i;
493                         findActionById(PAYMENT).setDescription(payment);
494                         notifyActionChanged(findActionPositionById(PAYMENT));
495                         updateContinue(isPasswordValid());
496                         break;
497                     }
498                 }
499                 return true;
500             } else {
501                 FragmentManager fm = getFragmentManager();
502                 NewPaymentStepFragment newPaymentFragment = new NewPaymentStepFragment();
503                 newPaymentFragment.setTargetFragment(this, 0);
504                 GuidedStepFragment.add(fm, newPaymentFragment, R.id.lb_guidedstep_host);
505                 return false;
506             }
507         }
508 
509         @Override
510         public @Nullable View onCreateView(@NonNull LayoutInflater inflater,
511                 @NonNull ViewGroup container, @Nullable Bundle savedInstanceState) {
512             View view = super.onCreateView(inflater, container, savedInstanceState);
513             if (mExpandPaymentListInOnCreateView) {
514                 expandAction(findActionById(PAYMENT), false);
515             }
516             return view;
517         }
518 
519         boolean isPaymentValid() {
520             CharSequence paymentType = findActionById(PAYMENT).getDescription();
521             return (paymentType.length() >= 4 &&
522                     paymentType.subSequence(0, 4).toString().equals("Visa")) ||
523                     (paymentType.length() >= 4 &&
524                     paymentType.subSequence(0, 4).toString().equals("AmEx"));
525         }
526 
527         boolean isPasswordValid() {
528             return findActionById(PASSWORD).getEditDescription().length() > 0;
529         }
530 
531         void updateContinue(boolean enabled) {
532             findButtonActionById(GuidedAction.ACTION_ID_CONTINUE).setEnabled(enabled);
533             notifyButtonActionChanged(findButtonActionPositionById(
534                     GuidedAction.ACTION_ID_CONTINUE));
535         }
536     }
537 
538     public static class ThirdStepFragment extends GuidedStepFragment {
539 
540         private long mSelectedOption = DEFAULT_OPTION;
541 
542         @Override
543         public @NonNull Guidance onCreateGuidance(@Nullable Bundle savedInstanceState) {
544             String title = getString(R.string.guidedstep_third_title);
545             String breadcrumb = getString(R.string.guidedstep_third_breadcrumb);
546             String description = getString(R.string.guidedstep_third_description);
547             final Context context = getActivity();
548             Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
549                     R.drawable.ic_main_icon, context.getTheme());
550             return new Guidance(title, description, breadcrumb, icon);
551         }
552 
553         @Override
554         public @NonNull GuidanceStylist onCreateGuidanceStylist() {
555             return new GuidanceStylist() {
556                 @Override
557                 public int onProvideLayoutId() {
558                     return R.layout.guidedstep_second_guidance;
559                 }
560             };
561         }
562 
563         @Override
564         public void onCreateActions(@NonNull List<GuidedAction> actions,
565                 @Nullable Bundle savedInstanceState) {
566             String desc = "The description can be quite long as well.  "
567                     + "Just be sure to set multilineDescription to true in the GuidedAction."
568                     + "For testing purpose we make this line even longer since "
569                     + "multilineDescriptionMinLines will be set to 2.";
570             actions.add(new GuidedAction.Builder(getActivity())
571                     .title("Note that Guided Actions can have titles that are quite long.")
572                     .description(desc)
573                     .multilineDescription(true)
574                     .infoOnly(true)
575                     .enabled(true)
576                     .focusable(false)
577                     .build());
578             for (int i = 0; i < OPTION_NAMES.length; i++) {
579                 addCheckedAction(actions, RADIO_ID_BASE + i, OPTION_NAMES[i],
580                         OPTION_DESCRIPTIONS[i], GuidedAction.DEFAULT_CHECK_SET_ID);
581                 if (i == DEFAULT_OPTION) {
582                     actions.get(actions.size() -1).setChecked(true);
583                 }
584             }
585             for (int i = 0; i < OPTION_NAMES.length; i++) {
586                 addCheckedAction(actions, CHECKBOX_ID_BASE + i, OPTION_NAMES[i],
587                         OPTION_DESCRIPTIONS[i], GuidedAction.CHECKBOX_CHECK_SET_ID);
588             }
589         }
590 
591         @Override
592         public void onCreateButtonActions(@NonNull List<GuidedAction> actions,
593                 @Nullable Bundle savedInstanceState) {
594             actions.add(new GuidedAction.Builder(getActivity())
595                     .clickAction(GuidedAction.ACTION_ID_CONTINUE)
596                     .build());
597         }
598 
599         @Override
600         public void onGuidedActionClicked(@NonNull GuidedAction action) {
601             if (action.getId() == GuidedAction.ACTION_ID_CONTINUE) {
602                 FragmentManager fm = getFragmentManager();
603                 FourthStepFragment f = new FourthStepFragment();
604                 Bundle arguments = new Bundle();
605                 arguments.putLong(FourthStepFragment.EXTRA_OPTION, mSelectedOption);
606                 f.setArguments(arguments);
607                 GuidedStepFragment.add(fm, f, R.id.lb_guidedstep_host);
608             } else if (action.getCheckSetId() == GuidedAction.DEFAULT_CHECK_SET_ID) {
609                 mSelectedOption = action.getId();
610             }
611         }
612 
613     }
614 
615     public static class FourthStepFragment extends GuidedStepFragment {
616         public static final String EXTRA_OPTION = "extra_option";
617 
618         public FourthStepFragment() {
619         }
620 
621         public long getOption() {
622             Bundle b = getArguments();
623             if (b == null) return 0;
624             return b.getLong(EXTRA_OPTION, 0);
625         }
626 
627         @Override
628         public @NonNull Guidance onCreateGuidance(@Nullable Bundle savedInstanceState) {
629             String title = getString(R.string.guidedstep_fourth_title);
630             String breadcrumb = getString(R.string.guidedstep_fourth_breadcrumb);
631             String description = "You chose: " + OPTION_NAMES[(int) getOption()];
632             final Context context = getActivity();
633             Drawable icon = ResourcesCompat.getDrawable(context.getResources(),
634                     R.drawable.ic_main_icon, context.getTheme());
635             return new Guidance(title, description, breadcrumb, icon);
636         }
637 
638         @Override
639         public void onCreateActions(@NonNull List<GuidedAction> actions,
640                 @Nullable Bundle savedInstanceState) {
641             actions.add(new GuidedAction.Builder(getActivity())
642                     .clickAction(GuidedAction.ACTION_ID_FINISH)
643                     .description("All Done...")
644                     .build());
645             addAction(actions, BACK, "Start Over", "Let's try this again...");
646         }
647 
648         @Override
649         public void onGuidedActionClicked(@NonNull GuidedAction action) {
650             if (action.getId() == GuidedAction.ACTION_ID_FINISH) {
651                 finishGuidedStepFragments();
652             } else if (action.getId() == BACK) {
653                 // pop 4, 3, 2
654                 popBackStackToGuidedStepFragment(SecondStepFragment.class,
655                         FragmentManager.POP_BACK_STACK_INCLUSIVE);
656             }
657         }
658 
659     }
660 
661 }
662