GuidedStepActivity.java revision 47005bba6e07c4578530bd151967c0cce7daedc3
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
17package com.example.android.leanback;
18
19import android.app.Activity;
20import android.app.FragmentManager;
21import android.content.Context;
22import android.content.Intent;
23import android.content.res.Configuration;
24import android.graphics.drawable.Drawable;
25import android.os.Bundle;
26import android.support.v17.leanback.app.GuidedStepFragment;
27import android.support.v17.leanback.widget.GuidedAction;
28import android.support.v17.leanback.widget.GuidedActionsStylist;
29import android.support.v17.leanback.widget.GuidanceStylist;
30import android.support.v17.leanback.widget.GuidanceStylist.Guidance;
31import android.support.v17.leanback.widget.GuidedActionsStylist.ViewHolder;
32import android.text.InputType;
33import android.text.TextUtils;
34import android.util.Log;
35import android.view.ViewGroup;
36import android.view.ViewTreeObserver.OnGlobalLayoutListener;
37import android.view.inputmethod.EditorInfo;
38
39import java.util.ArrayList;
40import java.util.List;
41
42/**
43 * Activity that showcases different aspects of GuidedStepFragments.
44 */
45public class GuidedStepActivity extends Activity {
46
47    private static final int BACK = 2;
48
49    private static final int FIRST_NAME = 3;
50    private static final int LAST_NAME = 4;
51    private static final int PASSWORD = 5;
52    private static final int PAYMENT = 6;
53    private static final int NEW_PAYMENT = 7;
54
55    private static final int OPTION_CHECK_SET_ID = 10;
56    private static final int DEFAULT_OPTION = 0;
57    private static final String[] OPTION_NAMES = { "Option A", "Option B", "Option C" };
58    private static final String[] OPTION_DESCRIPTIONS = { "Here's one thing you can do",
59            "Here's another thing you can do", "Here's one more thing you can do" };
60
61    private static final String TAG = GuidedStepActivity.class.getSimpleName();
62
63    @Override
64    protected void onCreate(Bundle savedInstanceState) {
65        Log.v(TAG, "onCreate");
66        super.onCreate(savedInstanceState);
67        setContentView(R.layout.guided_step_activity);
68        GuidedStepFragment.addAsRoot(this, new FirstStepFragment(), R.id.lb_guidedstep_host);
69    }
70
71    @Override
72    public void onConfigurationChanged(Configuration newConfig) {
73        Log.v(TAG, "onConfigurationChanged");
74        super.onConfigurationChanged(newConfig);
75    }
76
77    @Override
78    protected void onSaveInstanceState(Bundle outState) {
79        Log.v(TAG, "onSaveInstanceState");
80        super.onSaveInstanceState(outState);
81    }
82
83    @Override
84    protected void onRestoreInstanceState(Bundle savedInstanceState) {
85        Log.v(TAG, "onRestoreInstanceState");
86        super.onRestoreInstanceState(savedInstanceState);
87    }
88
89    private static void addAction(List<GuidedAction> actions, long id, String title, String desc) {
90        actions.add(new GuidedAction.Builder()
91                .id(id)
92                .title(title)
93                .description(desc)
94                .build());
95    }
96
97    private static void addAction(List<GuidedAction> actions, long id, String title, String desc,
98            List<GuidedAction> subActions) {
99        actions.add(new GuidedAction.Builder()
100                .id(id)
101                .title(title)
102                .description(desc)
103                .subActions(subActions)
104                .build());
105    }
106
107    private static void addEditableAction(List<GuidedAction> actions, long id, String title, String desc) {
108        actions.add(new GuidedAction.Builder()
109                .id(id)
110                .title(title)
111                .description(desc)
112                .editable(true)
113                .build());
114    }
115
116    private static void addEditableAction(List<GuidedAction> actions, long id, String title,
117            String editTitle, String desc) {
118        actions.add(new GuidedAction.Builder()
119                .id(id)
120                .title(title)
121                .editTitle(editTitle)
122                .description(desc)
123                .editable(true)
124                .build());
125    }
126
127    private static void addEditableAction(List<GuidedAction> actions, long id, String title,
128            String editTitle, int editInputType, String desc, String editDesc) {
129        actions.add(new GuidedAction.Builder()
130                .id(id)
131                .title(title)
132                .editTitle(editTitle)
133                .editInputType(editInputType)
134                .description(desc)
135                .editDescription(editDesc)
136                .editable(true)
137                .build());
138    }
139
140    private static void addEditableDescriptionAction(List<GuidedAction> actions, long id,
141            String title, String desc, String editDescription, int descriptionEditInputType) {
142        actions.add(new GuidedAction.Builder()
143                .id(id)
144                .title(title)
145                .description(desc)
146                .editDescription(editDescription)
147                .descriptionEditInputType(descriptionEditInputType)
148                .descriptionEditable(true)
149                .build());
150    }
151
152    private static void addCheckedAction(List<GuidedAction> actions, Context context,
153            String title, String desc, int checkSetId) {
154        actions.add(new GuidedAction.Builder()
155                .title(title)
156                .description(desc)
157                .checkSetId(checkSetId)
158                .build());
159    }
160
161    public static class FirstStepFragment extends GuidedStepFragment {
162        @Override
163        public int onProvideTheme() {
164            return R.style.Theme_Example_Leanback_GuidedStep_First;
165        }
166
167        @Override
168        public Guidance onCreateGuidance(Bundle savedInstanceState) {
169            String title = getString(R.string.guidedstep_first_title);
170            String breadcrumb = getString(R.string.guidedstep_first_breadcrumb);
171            String description = getString(R.string.guidedstep_first_description);
172            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
173            return new Guidance(title, description, breadcrumb, icon);
174        }
175
176        @Override
177        public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
178            Context context = getActivity();
179            actions.add(new GuidedAction.Builder(context)
180                    .clickAction(GuidedAction.ACTION_ID_CONTINUE)
181                    .description("Let's do it")
182                    .build());
183            actions.add(new GuidedAction.Builder(context)
184                    .clickAction(GuidedAction.ACTION_ID_CANCEL)
185                    .description("Never mind")
186                    .build());
187        }
188
189        @Override
190        public void onGuidedActionClicked(GuidedAction action) {
191            FragmentManager fm = getFragmentManager();
192            if (action.getId() == GuidedAction.ACTION_ID_CONTINUE) {
193                GuidedStepFragment.add(fm, new SecondStepFragment(), R.id.lb_guidedstep_host);
194            } else if (action.getId() == GuidedAction.ACTION_ID_CANCEL){
195                finishGuidedStepFragments();
196            }
197        }
198
199    }
200
201    static ArrayList<String> sCards = new ArrayList<String>();
202    static int sSelectedCard = -1;
203    static {
204        sCards.add("Visa-1234");
205        sCards.add("Master-4321");
206    }
207
208    public static class NewPaymentStepFragment extends GuidedStepFragment {
209
210        @Override
211        public Guidance onCreateGuidance(Bundle savedInstanceState) {
212            String title = getString(R.string.guidedstep_newpayment_title);
213            String breadcrumb = getString(R.string.guidedstep_newpayment_breadcrumb);
214            String description = getString(R.string.guidedstep_newpayment_description);
215            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
216            return new Guidance(title, description, breadcrumb, icon);
217        }
218
219        @Override
220        public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
221            addEditableAction(actions, NEW_PAYMENT, "Input credit card number", "",
222                    InputType.TYPE_CLASS_NUMBER,
223                    "Input credit card number", "Input credit card number");
224        }
225
226        @Override
227        public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
228            Context context = getActivity();
229            actions.add(new GuidedAction.Builder(context).clickAction(GuidedAction.ACTION_ID_OK)
230                    .build());
231            actions.get(actions.size() - 1).setEnabled(false);
232        }
233
234        @Override
235        public void onGuidedActionClicked(GuidedAction action) {
236            if (action.getId() == GuidedAction.ACTION_ID_OK) {
237                CharSequence desc = findActionById(NEW_PAYMENT).getDescription();
238                String cardNumber = desc.subSequence(desc.length() - 4, desc.length()).toString();
239                String card;
240                if ((Integer.parseInt(cardNumber) & 1) == 0) {
241                    card = "Visa "+cardNumber;
242                } else {
243                    card = "Master "+cardNumber;
244                }
245                sSelectedCard = sCards.size();
246                sCards.add(card);
247                popBackStackToGuidedStepFragment(NewPaymentStepFragment.class,
248                        FragmentManager.POP_BACK_STACK_INCLUSIVE);
249            }
250        }
251
252        @Override
253        public long onGuidedActionEditedAndProceed(GuidedAction action) {
254            if (action.getId() == NEW_PAYMENT) {
255                CharSequence editTitle = action.getEditTitle();
256                if (TextUtils.isDigitsOnly(editTitle) && editTitle.length() == 16) {
257                    editTitle = editTitle.subSequence(editTitle.length() - 4, editTitle.length());
258                    action.setDescription("Visa XXXX-XXXX-XXXX-" + editTitle);
259                    updateOkButton(true);
260                    return GuidedAction.ACTION_ID_NEXT;
261                } else if (editTitle.length() == 0) {
262                    action.setDescription("Input credit card number");
263                    updateOkButton(false);
264                    return GuidedAction.ACTION_ID_CURRENT;
265                } else {
266                    action.setDescription("Error credit card number");
267                    updateOkButton(false);
268                    return GuidedAction.ACTION_ID_CURRENT;
269                }
270            }
271            return GuidedAction.ACTION_ID_NEXT;
272        }
273
274        void updateOkButton(boolean enabled) {
275            findButtonActionById(GuidedAction.ACTION_ID_OK).setEnabled(enabled);
276            notifyButtonActionChanged(findButtonActionPositionById(GuidedAction.ACTION_ID_OK));
277        }
278    }
279
280    public static class SecondStepFragment extends GuidedStepFragment {
281
282        public GuidedActionsStylist onCreateActionsStylist() {
283            return new GuidedActionsStylist() {
284                protected void setupImeOptions(GuidedActionsStylist.ViewHolder vh,
285                        GuidedAction action) {
286                    if (action.getId() == PASSWORD) {
287                        vh.getEditableDescriptionView().setImeActionLabel("Confirm!",
288                                EditorInfo.IME_ACTION_DONE);
289                    } else {
290                        super.setupImeOptions(vh, action);
291                    }
292                }
293            };
294        }
295
296        @Override
297        public Guidance onCreateGuidance(Bundle savedInstanceState) {
298            String title = getString(R.string.guidedstep_second_title);
299            String breadcrumb = getString(R.string.guidedstep_second_breadcrumb);
300            String description = getString(R.string.guidedstep_second_description);
301            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
302            return new Guidance(title, description, breadcrumb, icon);
303        }
304
305        @Override
306        public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
307            addEditableAction(actions, FIRST_NAME, "Pat", "Your first name");
308            addEditableAction(actions, LAST_NAME, "Smith", "Your last name");
309            List<GuidedAction> subActions = new ArrayList<GuidedAction>();
310            addAction(actions, PAYMENT, "Select Payment", "", subActions);
311            addEditableDescriptionAction(actions, PASSWORD, "Password", "", "",
312                    InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
313        }
314
315        @Override
316        public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
317            actions.add(new GuidedAction.Builder(getActivity())
318                    .clickAction(GuidedAction.ACTION_ID_CONTINUE)
319                    .description("Continue")
320                    .build());
321        }
322
323        @Override
324        public void onGuidedActionClicked(GuidedAction action) {
325            if (action.getId() == GuidedAction.ACTION_ID_CONTINUE) {
326                FragmentManager fm = getFragmentManager();
327                GuidedStepFragment.add(fm, new ThirdStepFragment(), R.id.lb_guidedstep_host);
328            }
329        }
330
331        @Override
332        public long onGuidedActionEditedAndProceed(GuidedAction action) {
333            if (action.getId() == PASSWORD) {
334                CharSequence password = action.getEditDescription();
335                if (password.length() > 0) {
336                    if (isPaymentValid()) {
337                        updateContinue(true);
338                        return GuidedAction.ACTION_ID_NEXT;
339                    } else {
340                        updateContinue(false);
341                        return GuidedAction.ACTION_ID_CURRENT;
342                    }
343                } else {
344                    updateContinue(false);
345                    return GuidedAction.ACTION_ID_CURRENT;
346                }
347            }
348            return GuidedAction.ACTION_ID_NEXT;
349        }
350
351        @Override
352        public boolean onSubGuidedActionClicked(GuidedAction action) {
353            if (action.isChecked()) {
354                String payment = action.getTitle().toString();
355                for (int i = 0; i < sCards.size(); i++) {
356                    if (payment.equals(sCards.get(i))) {
357                        sSelectedCard = i;
358                        findActionById(PAYMENT).setDescription(payment);
359                        notifyActionChanged(findActionPositionById(PAYMENT));
360                        updateContinue(isPasswordValid());
361                        break;
362                    }
363                }
364                return true;
365            } else {
366                FragmentManager fm = getFragmentManager();
367                GuidedStepFragment.add(fm, new NewPaymentStepFragment(), R.id.lb_guidedstep_host);
368                return false;
369            }
370        }
371
372        @Override
373        public void onResume() {
374            super.onResume();
375            // when resumed, update sub actions list and selected index from data model.
376            GuidedAction payments = findActionById(PAYMENT);
377            payments.getSubActions().clear();
378            for (int i = 0; i < sCards.size(); i++) {
379                addCheckedAction(payments.getSubActions(), getActivity(), sCards.get(i), "",
380                        GuidedAction.DEFAULT_CHECK_SET_ID);
381                if (i == sSelectedCard) {
382                    payments.getSubActions().get(i).setChecked(true);
383                }
384            }
385            addAction(payments.getSubActions(), NEW_PAYMENT, "Add New Card", "");
386            if (sSelectedCard != -1) {
387                payments.setDescription(sCards.get(sSelectedCard));
388            }
389            notifyActionChanged(findActionPositionById(PAYMENT));
390            updateContinue(isPasswordValid() && isPaymentValid());
391        }
392
393        boolean isPaymentValid() {
394            CharSequence paymentType = findActionById(PAYMENT).getDescription();
395            return paymentType.subSequence(0, 4).toString().equals("Visa") ||
396                    paymentType.subSequence(0, 6).toString().equals("Master");
397        }
398
399        boolean isPasswordValid() {
400            return findActionById(PASSWORD).getEditDescription().length() > 0;
401        }
402
403        void updateContinue(boolean enabled) {
404            findButtonActionById(GuidedAction.ACTION_ID_CONTINUE).setEnabled(enabled);
405            notifyButtonActionChanged(findButtonActionPositionById(
406                    GuidedAction.ACTION_ID_CONTINUE));
407        }
408    }
409
410    public static class ThirdStepFragment extends GuidedStepFragment {
411
412        private int mSelectedOption = DEFAULT_OPTION;
413
414        @Override
415        public Guidance onCreateGuidance(Bundle savedInstanceState) {
416            String title = getString(R.string.guidedstep_third_title);
417            String breadcrumb = getString(R.string.guidedstep_third_breadcrumb);
418            String description = getString(R.string.guidedstep_third_description);
419            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
420            return new Guidance(title, description, breadcrumb, icon);
421        }
422
423        @Override
424        public GuidanceStylist onCreateGuidanceStylist() {
425            return new GuidanceStylist() {
426                @Override
427                public int onProvideLayoutId() {
428                    return R.layout.guidedstep_second_guidance;
429                }
430            };
431        }
432
433        @Override
434        public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
435            String desc = "The description can be quite long as well.  ";
436            desc += "Just be sure to set multilineDescription to true in the GuidedAction.";
437            actions.add(new GuidedAction.Builder()
438                    .title("Note that Guided Actions can have titles that are quite long.")
439                    .description(desc)
440                    .multilineDescription(true)
441                    .infoOnly(true)
442                    .enabled(true)
443                    .focusable(false)
444                    .build());
445            for (int i = 0; i < OPTION_NAMES.length; i++) {
446                addCheckedAction(actions, getActivity(), OPTION_NAMES[i],
447                        OPTION_DESCRIPTIONS[i], GuidedAction.DEFAULT_CHECK_SET_ID);
448                if (i == DEFAULT_OPTION) {
449                    actions.get(actions.size() -1).setChecked(true);
450                }
451            }
452            for (int i = 0; i < OPTION_NAMES.length; i++) {
453                addCheckedAction(actions, getActivity(), OPTION_NAMES[i],
454                        OPTION_DESCRIPTIONS[i], GuidedAction.CHECKBOX_CHECK_SET_ID);
455            }
456        }
457
458        @Override
459        public void onCreateButtonActions(List<GuidedAction> actions, Bundle savedInstanceState) {
460            actions.add(new GuidedAction.Builder(getActivity())
461                    .clickAction(GuidedAction.ACTION_ID_CONTINUE)
462                    .build());
463        }
464
465        @Override
466        public void onGuidedActionClicked(GuidedAction action) {
467            if (action.getId() == GuidedAction.ACTION_ID_CONTINUE) {
468                FragmentManager fm = getFragmentManager();
469                FourthStepFragment f = new FourthStepFragment();
470                Bundle arguments = new Bundle();
471                arguments.putInt(FourthStepFragment.EXTRA_OPTION, mSelectedOption);
472                f.setArguments(arguments);
473                GuidedStepFragment.add(fm, f, R.id.lb_guidedstep_host);
474            } else if (action.getCheckSetId() == GuidedAction.DEFAULT_CHECK_SET_ID) {
475                mSelectedOption = getSelectedActionPosition()-1;
476            }
477        }
478
479    }
480
481    public static class FourthStepFragment extends GuidedStepFragment {
482        public static final String EXTRA_OPTION = "extra_option";
483
484        public FourthStepFragment() {
485        }
486
487        public int getOption() {
488            Bundle b = getArguments();
489            if (b == null) return 0;
490            return b.getInt(EXTRA_OPTION, 0);
491        }
492
493        @Override
494        public Guidance onCreateGuidance(Bundle savedInstanceState) {
495            String title = getString(R.string.guidedstep_fourth_title);
496            String breadcrumb = getString(R.string.guidedstep_fourth_breadcrumb);
497            String description = "You chose: " + OPTION_NAMES[getOption()];
498            Drawable icon = getActivity().getResources().getDrawable(R.drawable.ic_main_icon);
499            return new Guidance(title, description, breadcrumb, icon);
500        }
501
502        @Override
503        public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
504            actions.add(new GuidedAction.Builder(getActivity())
505                    .clickAction(GuidedAction.ACTION_ID_FINISH)
506                    .description("All Done...")
507                    .build());
508            addAction(actions, BACK, "Start Over", "Let's try this again...");
509        }
510
511        @Override
512        public void onGuidedActionClicked(GuidedAction action) {
513            if (action.getId() == GuidedAction.ACTION_ID_FINISH) {
514                finishGuidedStepFragments();
515            } else if (action.getId() == BACK) {
516                // pop 4, 3, 2
517                popBackStackToGuidedStepFragment(SecondStepFragment.class,
518                        FragmentManager.POP_BACK_STACK_INCLUSIVE);
519            }
520        }
521
522    }
523
524}
525