SupportLeanbackShowcase: Added new credit card workflow and theme

background for the wizard view

Created a payment workflow consisting of a set of fragments for
entering & adding new credit card information (checks in place
for validity of the card number as well as expiration date).
Also, changed the dropdown subaction's background colors to
be consistent with the rest of guided step fragment view.
Fixed the back key pressed to "not" exit the entire wizard on the last
fragment (it not goes back to the payment fragment section when back
button is pressed).

Change-Id: I39cf316f09b765cffbd0a7e012539c49dbe238ed
This commit is contained in:
Keyvan Amiri
2016-02-09 11:53:03 -08:00
parent 285adeccf2
commit 64cae78d53
7 changed files with 213 additions and 18 deletions

View File

@@ -56,7 +56,7 @@ public class WizardExample1stStepFragment extends WizardExampleBaseStepFragment
.title(getString(R.string.wizard_example_rent_sd))
.editable(false)
.description(mMovie.getPriceSd() + " " +
R.string.wizard_example_watch_sd)
getString(R.string.wizard_example_watch_sd))
.build();
actions.add(action);
}

View File

@@ -14,6 +14,7 @@
package android.support.v17.leanback.supportleanbackshowcase.app.wizard;
import android.app.FragmentManager;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -26,6 +27,7 @@ import android.support.v17.leanback.widget.GuidedAction;
import android.support.v17.leanback.widget.GuidedActionsStylist;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
/**
@@ -36,6 +38,16 @@ public class WizardExample2ndStepFragment extends WizardExampleBaseStepFragment
private static final String ARG_HD = "hd";
private static final int ACTION_ID_CONFIRM = 1;
private static final int ACTION_ID_PAYMENT_METHOD = ACTION_ID_CONFIRM + 1;
private static final int ACTION_ID_NEW_PAYMENT = ACTION_ID_PAYMENT_METHOD + 1;
protected static ArrayList<String> sCards = new ArrayList<>();
protected static int sSelectedCard = -1;
static {
sCards.add("Visa-1234");
sCards.add("Master-4321");
}
public static GuidedStepFragment build(boolean hd, WizardExampleBaseStepFragment previousFragment) {
GuidedStepFragment fragment = new WizardExample2ndStepFragment();
@@ -66,27 +78,67 @@ public class WizardExample2ndStepFragment extends WizardExampleBaseStepFragment
.description(rentHighDefinition ? mMovie.getPriceHd() : mMovie.getPriceSd())
.editable(false)
.build();
action.setEnabled(false);
actions.add(action);
List<GuidedAction> subActions = new ArrayList<>();
action = new GuidedAction.Builder(getActivity())
.id(ACTION_ID_PAYMENT_METHOD)
.title(R.string.wizard_example_payment_method)
.editTitle("")
.description(R.string.wizard_example_input_credit)
.editable(true)
.subActions(subActions)
.build();
actions.add(action);
}
@Override
public void onGuidedActionEdited(GuidedAction action) {
CharSequence editTitle = action.getEditTitle();
if (TextUtils.isDigitsOnly(editTitle) && editTitle.length() == 16) {
action.setDescription(getString(R.string.wizard_example_visa,
editTitle.subSequence(editTitle.length() - 4, editTitle.length())));
} else if (editTitle.length() == 0) {
action.setDescription(getString(R.string.wizard_example_input_credit));
public void onResume() {
super.onResume();
GuidedAction payment = findActionById(ACTION_ID_PAYMENT_METHOD);
List<GuidedAction> paymentSubActions = payment.getSubActions();
paymentSubActions.clear();
for (int i = 0; i < sCards.size(); i++) {
paymentSubActions.add(new GuidedAction.Builder(getActivity())
.title(sCards.get(i))
.description("")
.checkSetId(GuidedAction.DEFAULT_CHECK_SET_ID)
.build()
);
}
paymentSubActions.add(new GuidedAction.Builder(getActivity())
.id(ACTION_ID_NEW_PAYMENT)
.title("Add New Card")
.description("")
.editable(false)
.build()
);
if ( sSelectedCard >= 0 && sSelectedCard < sCards.size() ) {
payment.setDescription(sCards.get(sSelectedCard));
findActionById(ACTION_ID_CONFIRM).setEnabled(true);
} else
findActionById(ACTION_ID_CONFIRM).setEnabled(false);
notifyActionChanged(findActionPositionById(ACTION_ID_CONFIRM));
}
@Override
public boolean onSubGuidedActionClicked(GuidedAction action) {
if (action.isChecked()) {
String payment = action.getTitle().toString();
if ( (sSelectedCard = sCards.indexOf(payment)) != -1 ) {
findActionById(ACTION_ID_PAYMENT_METHOD).setDescription(payment);
notifyActionChanged(findActionPositionById(ACTION_ID_PAYMENT_METHOD));
findActionById(ACTION_ID_CONFIRM).setEnabled(true);
notifyActionChanged(findActionPositionById(ACTION_ID_CONFIRM));
}
return true;
} else {
action.setDescription(getString(R.string.wizard_example_input_credit_wrong));
FragmentManager fm = getFragmentManager();
GuidedStepFragment fragment = new WizardNewPaymentStepFragment();
fragment.setArguments(getArguments());
add(fm, fragment);
return false;
}
}

View File

@@ -25,7 +25,8 @@ import android.support.v17.leanback.supportleanbackshowcase.R;
*/
public class WizardExampleActivity extends Activity {
@Override public void onCreate(Bundle savedInstanceState) {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setBackgroundDrawableResource(R.drawable.wizard_background_blackned);

View File

@@ -0,0 +1,135 @@
package android.support.v17.leanback.supportleanbackshowcase.app.wizard;
import android.app.FragmentManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v17.leanback.supportleanbackshowcase.R;
import android.support.v17.leanback.widget.GuidanceStylist;
import android.support.v17.leanback.widget.GuidedAction;
import android.support.v17.leanback.widget.GuidedDatePickerAction;
import android.text.TextUtils;
import java.util.Calendar;
import java.util.List;
/**
* Created by keyvana on 2/8/16.
*/
public class WizardNewPaymentStepFragment extends WizardExampleBaseStepFragment {
private static final int ACTION_ID_CARD_NUMBER = 1;
private static final int ACTION_ID_PAYMENT_EXP = ACTION_ID_CARD_NUMBER + 1;
@NonNull
@Override
public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
String title = getString(R.string.wizard_example_new_payment_guidance_title);
String description = getString(R.string.wizard_example_new_payment_guidance_description);
String breadcrumb = mMovie.getBreadcrump();
GuidanceStylist.Guidance guidance = new GuidanceStylist.Guidance(title, description,
breadcrumb, null);
return guidance;
}
@Override
public void onCreateActions(List<GuidedAction> actions, Bundle savedInstanceState) {
actions.add(new GuidedAction.Builder(getActivity())
.id(ACTION_ID_CARD_NUMBER)
.title(R.string.wizard_example_input_card)
.editTitle("")
.description(R.string.wizard_example_input_card)
.editDescription("Card number")
.editable(true)
.build()
);
actions.add(new GuidedDatePickerAction.Builder(getActivity())
.id(ACTION_ID_PAYMENT_EXP)
.title(R.string.wizard_example_expiration_date)
.datePickerFormat("MY")
.build()
);
}
@Override
public void onCreateButtonActions(@NonNull List<GuidedAction> actions,
Bundle savedInstanceState) {
actions.add(new GuidedAction.Builder(getActivity())
.clickAction(GuidedAction.ACTION_ID_OK)
.build()
);
actions.get(actions.size() - 1).setEnabled(false);
}
@Override
public void onGuidedActionClicked(GuidedAction action) {
if (action.getId() == GuidedAction.ACTION_ID_OK) {
CharSequence cardNumber = findActionById(ACTION_ID_CARD_NUMBER).getDescription();
WizardExample2ndStepFragment.sSelectedCard = WizardExample2ndStepFragment.sCards.size();
WizardExample2ndStepFragment.sCards.add(cardNumber.toString());
popBackStackToGuidedStepFragment(WizardNewPaymentStepFragment.class,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
@Override
public long onGuidedActionEditedAndProceed(GuidedAction action) {
boolean cardNumberCheck = false;
boolean expDateCheck = false;
if (action.getId() == ACTION_ID_CARD_NUMBER) {
CharSequence cardNumber = action.getEditTitle();
cardNumberCheck = isCardNumberValid(cardNumber);
expDateCheck = isExpDateValid(findActionById(ACTION_ID_PAYMENT_EXP));
updateOkButton(cardNumberCheck && expDateCheck);
if (cardNumberCheck) {
String last4Digits = cardNumber.subSequence(cardNumber.length() - 4,
cardNumber.length()).toString();
if ( (Integer.parseInt(last4Digits) & 1) == 0 )
action.setDescription(getString(R.string.wizard_example_visa,
last4Digits));
else
action.setDescription(getString(R.string.wizard_example_master,
last4Digits));
return GuidedAction.ACTION_ID_NEXT;
} else if (cardNumber.length() == 0) {
action.setDescription(getString(R.string.wizard_example_input_card));
return GuidedAction.ACTION_ID_CURRENT;
} else {
action.setDescription(getString(R.string.wizard_example_input_credit_wrong));
return GuidedAction.ACTION_ID_CURRENT;
}
} else if (action.getId() == ACTION_ID_PAYMENT_EXP) {
expDateCheck = isExpDateValid(action);
cardNumberCheck = isCardNumberValid(findActionById(ACTION_ID_CARD_NUMBER)
.getEditTitle());
updateOkButton(cardNumberCheck && expDateCheck);
if (expDateCheck) {
return GuidedAction.ACTION_ID_NEXT;
}
}
return GuidedAction.ACTION_ID_CURRENT;
}
private void updateOkButton(boolean enabled) {
findButtonActionById(GuidedAction.ACTION_ID_OK).setEnabled(enabled);
notifyButtonActionChanged(findButtonActionPositionById(GuidedAction.ACTION_ID_OK));
}
private static boolean isCardNumberValid(CharSequence number) {
return (TextUtils.isDigitsOnly(number) && number.length() == 16);
}
private static boolean isExpDateValid(GuidedAction dateAction) {
long date = ((GuidedDatePickerAction) dateAction).getDate();
Calendar c = Calendar.getInstance();
c.setTimeInMillis(date);
return Calendar.getInstance().before(c);
}
}

View File

@@ -42,6 +42,7 @@
<color name="detail_view_background">#0374BF</color>
<color name="detail_view_related_background">#022A4E</color>
<color name="guidedstep_actions_background">#C03800</color>
<color name="guidedstep_dialog_actions_background">#263238</color>
<color name="app_guidedstep_actions_background">#C03800</color>
<color name="app_guidedstep_subactions_background">#C03800</color>
<color name="app_guidedstep_dialog_actions_background">#263238</color>
</resources>

View File

@@ -42,6 +42,8 @@
<string name="wizard_example_watch_hd">Watch in HD on supported devices</string>
<string name="wizard_example_watch_sd">Watch in standard definition on the web and supported devices</string>
<string name="wizard_example_rental_period">Rental period: start within 30 days,\nfinish within 24 hours</string>
<string name="wizard_example_input_card">Enter credit card number</string>
<string name="wizard_example_expiration_date">Exp. date</string>
<string name="wizard_example_payment_method">Payment Method</string>
<string name="wizard_example_toast_payment_method_clicked">\'Payment Method\' clicked.</string>
<string name="wizard_example_rent">Rent</string>
@@ -51,8 +53,11 @@
<string name="wizard_example_watch_now">Watch now</string>
<string name="wizard_example_later">Later</string>
<string name="wizard_example_watch_now_clicked">\'Watch now\' clicked.</string>
<string name="wizard_example_input_credit">Input credit card number</string>
<string name="wizard_example_visa">Visa XXXX-XXXX-XXXX-%s</string>
<string name="wizard_example_input_credit_wrong">Error credit card number</string>
<string name="wizard_example_new_payment_guidance_title">New credit card</string>
<string name="wizard_example_new_payment_guidance_description">Enter new credit card information here</string>
<string name="wizard_example_input_credit">Input Payment Type</string>
<string name="wizard_example_visa">Visa-%s</string>
<string name="wizard_example_master">Master-%s</string>
<string name="wizard_example_input_credit_wrong">Error: invalid credit card number</string>
s<string name="wizard_example_just_a_second">Just a second...</string>
</resources>

View File

@@ -35,7 +35,8 @@
</style>
<style name="Theme.Example.LeanbackWizard" parent="Theme.Leanback.GuidedStep">
<item name="guidedActionsBackground">@color/guidedstep_actions_background</item>
<item name="guidedActionsBackground">@color/app_guidedstep_actions_background</item>
<item name="guidedActionsBackgroundDark">@color/app_guidedstep_subactions_background</item>
</style>
<style name="Theme.Example.LeanbackWizard.NoSelector">
@@ -43,7 +44,7 @@
</style>
<style name="Theme.Example.LeanbackDialog" parent="Theme.Leanback.GuidedStep">
<item name="guidedActionsBackground">@color/guidedstep_dialog_actions_background</item>
<item name="guidedActionsBackground">@color/app_guidedstep_dialog_actions_background</item>
</style>
<style name="Theme.Example.LeanbackPreferences" parent="Theme.Leanback">