1 /* 2 * Copyright (C) 2017 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 package com.example.android.autofill.app; 17 18 import android.app.DatePickerDialog; 19 import android.app.Dialog; 20 import android.content.Context; 21 import android.os.Bundle; 22 import android.support.annotation.NonNull; 23 import android.support.annotation.Nullable; 24 import android.support.v4.app.DialogFragment; 25 import android.support.v4.app.FragmentManager; 26 import android.support.v7.widget.AppCompatEditText; 27 import android.text.format.DateFormat; 28 import android.util.AttributeSet; 29 import android.util.Log; 30 import android.view.View; 31 import android.view.autofill.AutofillValue; 32 import android.widget.DatePicker; 33 34 import java.util.Calendar; 35 import java.util.Date; 36 37 import static com.example.android.autofill.app.CommonUtil.DEBUG; 38 import static com.example.android.autofill.app.CommonUtil.TAG; 39 40 /** 41 * A custom view that represents a {@link View#AUTOFILL_HINT_CREDIT_CARD_EXPIRATION_DATE} using 42 * a non-editable {@link EditText} that triggers a {@link DatePickerDialog} to represent the 43 * credit card expiration month and year. 44 */ 45 public class CreditCardExpirationDatePickerView extends AppCompatEditText { 46 47 private static final int CC_EXP_YEARS_COUNT = 5; 48 49 /** 50 * Calendar instance used for month / year calculations. Should be reset before each use. 51 */ 52 private final Calendar mTempCalendar; 53 54 private int mMonth; 55 private int mYear; 56 57 public CreditCardExpirationDatePickerView(@NonNull Context context) { 58 this(context, null); 59 } 60 61 public CreditCardExpirationDatePickerView(@NonNull Context context, 62 @Nullable AttributeSet attrs) { 63 this(context, attrs, 0); 64 } 65 66 public CreditCardExpirationDatePickerView(@NonNull Context context, 67 @Nullable AttributeSet attrs, int defStyleAttr) { 68 super(context, attrs, defStyleAttr); 69 // Use the current date as the initial date in the picker. 70 mTempCalendar = Calendar.getInstance(); 71 mYear = mTempCalendar.get(Calendar.YEAR); 72 mMonth = mTempCalendar.get(Calendar.MONTH); 73 } 74 75 /** 76 * Gets a temporary calendar set with the View's year and month. 77 */ 78 private Calendar getCalendar() { 79 mTempCalendar.clear(); 80 mTempCalendar.set(Calendar.YEAR, mYear); 81 mTempCalendar.set(Calendar.MONTH, mMonth); 82 mTempCalendar.set(Calendar.DATE, 1); 83 return mTempCalendar; 84 } 85 86 @Override 87 public AutofillValue getAutofillValue() { 88 Calendar c = getCalendar(); 89 AutofillValue value = AutofillValue.forDate(c.getTimeInMillis()); 90 if (DEBUG) Log.d(TAG, "getAutofillValue(): " + value); 91 return value; 92 } 93 94 @Override 95 public void autofill(AutofillValue value) { 96 if (value == null || !value.isDate()) { 97 Log.w(TAG, "autofill(): invalid value " + value); 98 return; 99 } 100 long time = value.getDateValue(); 101 mTempCalendar.setTimeInMillis(time); 102 int year = mTempCalendar.get(Calendar.YEAR); 103 int month = mTempCalendar.get(Calendar.MONTH); 104 if (DEBUG) Log.d(TAG, "autofill(" + value + "): " + month + "/" + year); 105 setDate(year, month); 106 } 107 108 private void setDate(int year, int month) { 109 mYear = year; 110 mMonth = month; 111 Date selectedDate = new Date(getCalendar().getTimeInMillis()); 112 String dateString = DateFormat.getDateFormat(getContext()).format(selectedDate); 113 setText(dateString); 114 } 115 116 @Override 117 public int getAutofillType() { 118 return AUTOFILL_TYPE_DATE; 119 } 120 121 public void reset() { 122 mTempCalendar.setTimeInMillis(System.currentTimeMillis()); 123 setDate(mTempCalendar.get(Calendar.YEAR), mTempCalendar.get(Calendar.MONTH)); 124 } 125 126 public void showDatePickerDialog(FragmentManager fragmentManager) { 127 DatePickerFragment newFragment = new DatePickerFragment(); 128 newFragment.mParent = this; 129 newFragment.show(fragmentManager, "datePicker"); 130 } 131 132 public static class DatePickerFragment extends DialogFragment 133 implements DatePickerDialog.OnDateSetListener { 134 135 private CreditCardExpirationDatePickerView mParent; 136 137 @Override 138 public Dialog onCreateDialog(Bundle savedInstanceState) { 139 DatePickerDialog dialog = new DatePickerDialog(getActivity(), 140 R.style.CustomDatePickerDialogTheme, this, mParent.mYear, mParent.mMonth, 1); 141 142 DatePicker datePicker = dialog.getDatePicker(); 143 144 // Limit range. 145 Calendar c = mParent.getCalendar(); 146 datePicker.setMinDate(c.getTimeInMillis()); 147 c.set(Calendar.YEAR, mParent.mYear + CC_EXP_YEARS_COUNT - 1); 148 datePicker.setMaxDate(c.getTimeInMillis()); 149 150 // Remove day. 151 datePicker.findViewById(getResources().getIdentifier("day", "id", "android")) 152 .setVisibility(View.GONE); 153 return dialog; 154 } 155 156 @Override 157 public void onDateSet(DatePicker view, int year, int month, int day) { 158 mParent.setDate(year, month); 159 } 160 } 161 }