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