├── settings.gradle
├── .DS_Store
├── paymentKitDroid
├── src
│ └── main
│ │ ├── java
│ │ └── com
│ │ │ ├── .DS_Store
│ │ │ └── paymentkit
│ │ │ ├── PaymentCard.java
│ │ │ ├── views
│ │ │ ├── InterceptEditText.java
│ │ │ ├── CVVEditText.java
│ │ │ ├── CardIcon.java
│ │ │ ├── CardNumHolder.java
│ │ │ ├── ExpirationEditText.java
│ │ │ ├── CardNumEditText.java
│ │ │ └── FieldHolder.java
│ │ │ ├── CardType.java
│ │ │ ├── util
│ │ │ ├── ToastUtils.java
│ │ │ └── ViewUtils.java
│ │ │ └── ValidateCreditCard.java
│ │ ├── res
│ │ ├── drawable-hdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── checkout_card_cvc.png
│ │ │ ├── checkout_card_amex.png
│ │ │ ├── checkout_card_master.png
│ │ │ ├── checkout_card_paypal.png
│ │ │ ├── checkout_card_visa.png
│ │ │ ├── textfield_default.9.png
│ │ │ ├── textfield_disabled.9.png
│ │ │ ├── textfield_pressed.9.png
│ │ │ ├── textfield_selected.9.png
│ │ │ ├── checkout_card_discover.png
│ │ │ ├── checkout_default_card.png
│ │ │ ├── checkout_payment_icons.png
│ │ │ ├── checkout_accepted_cards.png
│ │ │ ├── textfield_pressed_holo.9.png
│ │ │ ├── textfield_longpress_holo.9.png
│ │ │ ├── textfield_search_default.9.png
│ │ │ ├── textfield_search_pressed.9.png
│ │ │ ├── textfield_search_selected.9.png
│ │ │ ├── textfield_default_holo_dark.9.png
│ │ │ ├── textfield_default_holo_light.9.png
│ │ │ ├── textfield_disabled_holo_dark.9.png
│ │ │ ├── textfield_disabled_selected.9.png
│ │ │ ├── textfield_focused_holo_dark.9.png
│ │ │ ├── textfield_focused_holo_light.9.png
│ │ │ ├── textfield_activated_holo_dark.9.png
│ │ │ ├── textfield_activated_holo_light.9.png
│ │ │ ├── textfield_bg_default_holo_dark.9.png
│ │ │ ├── textfield_bg_focused_holo_dark.9.png
│ │ │ ├── textfield_disabled_holo_light.9.png
│ │ │ ├── textfield_search_empty_default.9.png
│ │ │ ├── textfield_search_empty_pressed.9.png
│ │ │ ├── textfield_bg_activated_holo_dark.9.png
│ │ │ ├── textfield_bg_disabled_holo_dark.9.png
│ │ │ ├── textfield_search_empty_selected.9.png
│ │ │ ├── textfield_search_default_holo_dark.9.png
│ │ │ ├── textfield_search_default_holo_light.9.png
│ │ │ ├── textfield_disabled_focused_holo_dark.9.png
│ │ │ ├── textfield_disabled_focused_holo_light.9.png
│ │ │ ├── textfield_multiline_default_holo_dark.9.png
│ │ │ ├── textfield_multiline_default_holo_light.9.png
│ │ │ ├── textfield_multiline_disabled_holo_dark.9.png
│ │ │ ├── textfield_multiline_focused_holo_dark.9.png
│ │ │ ├── textfield_multiline_focused_holo_light.9.png
│ │ │ ├── textfield_bg_disabled_focused_holo_dark.9.png
│ │ │ ├── textfield_multiline_activated_holo_dark.9.png
│ │ │ ├── textfield_multiline_activated_holo_light.9.png
│ │ │ ├── textfield_multiline_disabled_holo_light.9.png
│ │ │ ├── textfield_multiline_disabled_focused_holo_dark.9.png
│ │ │ └── textfield_multiline_disabled_focused_holo_light.9.png
│ │ ├── drawable-mdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── checkout_card_cvc.png
│ │ │ ├── checkout_card_amex.png
│ │ │ ├── checkout_card_master.png
│ │ │ ├── checkout_card_paypal.png
│ │ │ ├── checkout_card_visa.png
│ │ │ ├── checkout_card_discover.png
│ │ │ ├── checkout_default_card.png
│ │ │ └── checkout_payment_icons.png
│ │ ├── drawable-xhdpi
│ │ │ ├── ic_launcher.png
│ │ │ ├── checkout_card_amex.png
│ │ │ ├── checkout_card_cvc.png
│ │ │ ├── checkout_card_visa.png
│ │ │ ├── checkout_card_master.png
│ │ │ ├── checkout_card_paypal.png
│ │ │ ├── checkout_default_card.png
│ │ │ ├── checkout_card_discover.png
│ │ │ └── checkout_payment_icons.png
│ │ ├── drawable-ldpi
│ │ │ ├── checkout_card_cvc.png
│ │ │ ├── checkout_card_amex.png
│ │ │ ├── checkout_card_master.png
│ │ │ ├── checkout_card_paypal.png
│ │ │ ├── checkout_card_visa.png
│ │ │ ├── checkout_card_discover.png
│ │ │ ├── checkout_default_card.png
│ │ │ └── checkout_payment_icons.png
│ │ ├── values
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ ├── colors.xml
│ │ │ └── styles.xml
│ │ ├── drawable
│ │ │ ├── checkout_header_gradient.xml
│ │ │ ├── toast_background.xml
│ │ │ ├── checkout_next_btn_bg.xml
│ │ │ ├── edit_text.xml
│ │ │ └── edit_text_holo_light.xml
│ │ └── layout
│ │ │ ├── toast.xml
│ │ │ ├── card_holder.xml
│ │ │ └── field_holder.xml
│ │ └── AndroidManifest.xml
├── libs
│ └── nineoldandroids-2.4.0.jar
└── build.gradle
├── pKExample
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable-hdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-mdpi
│ │ │ └── ic_launcher.png
│ │ ├── drawable-xhdpi
│ │ │ └── ic_launcher.png
│ │ ├── values
│ │ │ ├── dimens.xml
│ │ │ ├── strings.xml
│ │ │ └── styles.xml
│ │ └── layout
│ │ │ ├── activity_main.xml
│ │ │ └── add_credit_card.xml
│ │ ├── java
│ │ └── com
│ │ │ └── brendan
│ │ │ └── pkexample
│ │ │ ├── PKActivity.java
│ │ │ └── PKFragment.java
│ │ └── AndroidManifest.xml
└── build.gradle
├── .gitignore
├── LICENSE
├── README.md
└── import-summary.txt
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':paymentKitDroid'
2 | include ':pKExample'
3 |
--------------------------------------------------------------------------------
/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/.DS_Store
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/java/com/.DS_Store
--------------------------------------------------------------------------------
/paymentKitDroid/libs/nineoldandroids-2.4.0.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/libs/nineoldandroids-2.4.0.jar
--------------------------------------------------------------------------------
/pKExample/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/pKExample/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/pKExample/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/pKExample/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/pKExample/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/pKExample/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_cvc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_cvc.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_cvc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_cvc.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_cvc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_cvc.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_amex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_amex.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_master.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_master.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_paypal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_paypal.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_visa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_visa.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_default.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_default.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_pressed.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_selected.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_amex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_amex.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_master.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_master.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_paypal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_paypal.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_visa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_visa.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_amex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_amex.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_master.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_master.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_paypal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_paypal.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_visa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_visa.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_amex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_amex.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_cvc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_cvc.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_visa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_visa.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_discover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/checkout_card_discover.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/checkout_default_card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/checkout_default_card.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/checkout_payment_icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/checkout_payment_icons.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_discover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-ldpi/checkout_card_discover.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-ldpi/checkout_default_card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-ldpi/checkout_default_card.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-ldpi/checkout_payment_icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-ldpi/checkout_payment_icons.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_discover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-mdpi/checkout_card_discover.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-mdpi/checkout_default_card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-mdpi/checkout_default_card.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-mdpi/checkout_payment_icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-mdpi/checkout_payment_icons.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_master.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_master.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_paypal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_paypal.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_default_card.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_default_card.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/checkout_accepted_cards.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/checkout_accepted_cards.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_pressed_holo.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_pressed_holo.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_discover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_card_discover.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_payment_icons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-xhdpi/checkout_payment_icons.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_longpress_holo.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_longpress_holo.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_default.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_default.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_pressed.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_selected.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_default_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_default_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_default_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_default_holo_light.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled_selected.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_focused_holo_light.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_activated_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_activated_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_activated_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_activated_holo_light.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_bg_default_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_bg_default_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_bg_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_bg_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled_holo_light.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_empty_default.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_empty_default.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_empty_pressed.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_empty_pressed.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_bg_activated_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_bg_activated_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_bg_disabled_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_bg_disabled_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_empty_selected.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_empty_selected.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_default_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_default_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_default_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_search_default_holo_light.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_disabled_focused_holo_light.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_default_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_default_holo_light.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_disabled_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_focused_holo_light.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_bg_disabled_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_bg_disabled_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_activated_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_activated_holo_light.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_disabled_holo_light.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_dark.9.png
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Dimillian/PaymentKit-Droid/master/paymentKitDroid/src/main/res/drawable-hdpi/textfield_multiline_disabled_focused_holo_light.9.png
--------------------------------------------------------------------------------
/pKExample/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
--------------------------------------------------------------------------------
/pKExample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PK Example
5 | Settings
6 | Hello world!
7 |
8 |
--------------------------------------------------------------------------------
/pKExample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 | 18sp
7 |
8 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable/checkout_header_gradient.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable/toast_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | PaymentKit
5 |
6 | Please enter a valid card number
7 |
8 | Credit Card Expiration Field
9 | MM/YY
10 |
11 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | #ff444444
5 | #FFFFFF
6 | #BB000000
7 | #1f4579
8 | #80000000
9 | #00000000
10 |
11 |
--------------------------------------------------------------------------------
/paymentKitDroid/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'android-library'
2 |
3 | android {
4 | compileSdkVersion 19
5 | buildToolsVersion "19.0.1"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 19
10 | }
11 |
12 | release {
13 | runProguard false
14 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
15 | }
16 | }
17 |
18 | dependencies {
19 | compile files('libs/nineoldandroids-2.4.0.jar')
20 | }
21 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable/checkout_next_btn_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # built application files
2 | *.apk
3 | *.ap_
4 |
5 | # files for the dex VM
6 | *.dex
7 |
8 | # Java class files
9 | *.class
10 |
11 | # generated files
12 | bin/
13 | gen/
14 |
15 | # Local configuration file (sdk path, etc)
16 | local.properties
17 |
18 | # Eclipse project files
19 | .classpath
20 | .project
21 |
22 | # Android Studio
23 | /.idea/
24 | build
25 | out
26 | *~
27 | *.iml
28 | *.swp
29 |
30 | # Gradle files
31 | gradlew
32 | gradlew.bat
33 | gradle/
34 | .gradle
35 | build/
36 |
--------------------------------------------------------------------------------
/pKExample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'android'
2 |
3 | android {
4 | compileSdkVersion 19
5 | buildToolsVersion "19.0.1"
6 |
7 | defaultConfig {
8 | minSdkVersion 15
9 | targetSdkVersion 19
10 | }
11 |
12 | buildTypes {
13 | release {
14 | runProguard false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
16 | }
17 | }
18 | }
19 |
20 | dependencies {
21 | compile project(':paymentKitDroid')
22 | compile 'com.android.support:support-v4:+'
23 | }
24 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/PaymentCard.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit;
2 |
3 | public class PaymentCard {
4 |
5 | private String mLastFourDigits;
6 | private CardType mCardType;
7 | private String mExpMonth;
8 | private String mExpYear;
9 |
10 | public String getExpirationMonth() {
11 | return mExpMonth;
12 | }
13 |
14 | public String getExpirationYear() {
15 | return mExpYear;
16 | }
17 |
18 | public CardType getCardType() {
19 | return mCardType;
20 | }
21 |
22 | public String getLastFourDigits() {
23 | return mLastFourDigits;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/layout/toast.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
17 |
18 |
--------------------------------------------------------------------------------
/pKExample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/pKExample/src/main/java/com/brendan/pkexample/PKActivity.java:
--------------------------------------------------------------------------------
1 | package com.brendan.pkexample;
2 |
3 | import android.os.Bundle;
4 | import android.support.v4.app.FragmentActivity;
5 | import android.support.v4.app.FragmentTransaction;
6 | import android.view.Window;
7 |
8 | /**
9 | *
10 | * @author Brendan Weinstein
11 | * http://www.brendanweinstein.me
12 | *
13 | */
14 | public class PKActivity extends FragmentActivity {
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 | getWindow().requestFeature(Window.FEATURE_NO_TITLE);
20 | setContentView(R.layout.activity_main);
21 | FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
22 | ft.add(R.id.fragment_holder, new PKFragment(), "CardFragment");
23 | ft.commit();
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/pKExample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/layout/card_holder.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
13 |
14 |
26 |
27 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/views/InterceptEditText.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit.views;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.MotionEvent;
6 | import android.view.View;
7 | import android.widget.EditText;
8 |
9 |
10 | public class InterceptEditText extends EditText {
11 |
12 | private static final String TAG = InterceptEditText.class.getSimpleName();
13 |
14 | public InterceptEditText(Context context) {
15 | super(context);
16 | setup();
17 | }
18 |
19 | public InterceptEditText(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 | setup();
22 | }
23 |
24 | private void setup() {
25 | setOnClickListener(new OnClickListener() {
26 | @Override
27 | public void onClick(View v) {
28 |
29 | }
30 | });
31 | }
32 |
33 | @Override
34 | public boolean onTouchEvent(MotionEvent e) {
35 | switch(e.getAction()) {
36 | case MotionEvent.ACTION_DOWN:
37 | return true;
38 | case MotionEvent.ACTION_UP:
39 | performClick();
40 | }
41 | return true;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
23 |
24 |
29 |
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Brendan
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | PaymentKit-Droid
2 | ================
3 |
4 | PaymentKit-Droid is an Open Source Android library that allows developers to create sleek payment forms like those made
5 | popular by StubHub, Stripe, Uber, and Postmates. The library provides support for as far back as 2.1.
6 |
7 | The PKExample provides an example implementation pattern you may follow.
8 |
9 | The 'PaymentKit Droid' folder is the library project you want to add to
10 | your Android project. See http://stackoverflow.com/questions/8248196/how-to-add-a-library-project-to-a-android-project.
11 |
12 | A sample app will soon be available on the google play store.
13 |
14 | Feel free to leave requests and feedback through github (brendanw) or twitter (elbrendan).
15 |
16 | You can also find my contact information at http://www.brendanweinstein.me
17 |
18 | Things to be added in the future:
19 | -Documentation
20 | -Better handling of spacing for landscape mode
21 | -Support for more cards
22 | -Removal of edittext assets and the addition of code for using default android edittext backgrounds
23 | -Ability to move cursor anywhere in expiration date field.
24 | -More error messaging ability options
25 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/CardType.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit;
2 |
3 | public enum CardType {
4 | VISA(1, "Visa", false, 3),
5 | MASTERCARD(2, "Mastercard", false, 3),
6 | AMERICAN_EXPRESS(3, "American Express", false, 4),
7 | DISCOVER(4,"Discover", false, 3),
8 | UNKNOWN_CARD(5, "Unknown", true, 3),
9 | TOO_MANY_DIGITS(6, "Too Many Digits", true, 3),
10 | NOT_ENOUGH_DIGITS(7, "Not Enough Digits", true, 3);
11 |
12 | private int mVal;
13 | private String mName;
14 | private boolean mIsError;
15 | private int mMaxCVVLength;
16 |
17 | CardType(int val, String name, boolean isError, int maxCVVLength) {
18 | this.mVal = val;
19 | this.mName = name;
20 | this.mIsError = isError;
21 | }
22 |
23 | public int getValue() {
24 | return mVal;
25 | }
26 |
27 | public String getName() {
28 | return mName;
29 | }
30 |
31 | public boolean isError() {
32 | return mIsError;
33 | }
34 |
35 | public int getMaxCVVLength() {
36 | return mMaxCVVLength;
37 | }
38 |
39 | public static CardType fromString(String text) {
40 | if (text != null) {
41 | int num = Integer.parseInt(text);
42 | for (CardType c : CardType.values()) {
43 | if (c.mVal == num) {
44 | return c;
45 | }
46 | }
47 | }
48 | return CardType.UNKNOWN_CARD;
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable/edit_text.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
20 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/util/ToastUtils.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit.util;
2 |
3 | import com.paymentkit.R;
4 |
5 | import android.app.Activity;
6 | import android.content.Context;
7 | import android.view.Gravity;
8 | import android.view.LayoutInflater;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.widget.TextView;
12 | import android.widget.Toast;
13 |
14 | public class ToastUtils {
15 |
16 | public static void showLongToast(Activity activity, int textResId) {
17 | String text = activity.getResources().getString(textResId);
18 | showLongToast(activity, text);
19 | }
20 |
21 | public static void showToast(Activity activity, int textResId) {
22 | String text = activity.getResources().getString(textResId);
23 | showToast(activity, text);
24 | }
25 |
26 | public static void showLongToast(Activity activity, String text) {
27 | showToast(activity, text, Toast.LENGTH_LONG);
28 | }
29 |
30 | public static void showToast(Activity activity, String text) {
31 | showToast(activity, text, Toast.LENGTH_SHORT);
32 | }
33 |
34 | public static void showToast(Context context, String text) {
35 | showToast((Activity) context, text, Toast.LENGTH_SHORT);
36 | }
37 |
38 | public static void showToast(Activity activity, String text, int duration) {
39 | Toast toast = makeToast(activity, text, duration);
40 | toast.show();
41 | }
42 |
43 | public static Toast makeToast(Activity activity, int textResId, int duration) {
44 | String text = activity.getResources().getString(textResId);
45 | return makeToast(activity, text, duration);
46 | }
47 |
48 | private static Toast makeToast(Activity activity, String text, int duration) {
49 | LayoutInflater inflater = activity.getLayoutInflater();
50 | View layout = inflater.inflate(R.layout.toast, (ViewGroup) activity.findViewById(R.id.toast_layout_root));
51 | TextView textView = (TextView) layout.findViewById(R.id.text);
52 | textView.setText(text);
53 | Toast toast = new Toast(activity);
54 | toast.setGravity(Gravity.CENTER, 0, 0);
55 | toast.setDuration(duration);
56 | toast.setView(layout);
57 | return toast;
58 | }
59 | }
--------------------------------------------------------------------------------
/pKExample/src/main/java/com/brendan/pkexample/PKFragment.java:
--------------------------------------------------------------------------------
1 | package com.brendan.pkexample;
2 |
3 | import android.os.Build;
4 | import android.os.Bundle;
5 | import android.support.v4.app.Fragment;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.View.OnClickListener;
9 | import android.view.ViewGroup;
10 | import android.widget.Button;
11 | import android.widget.ImageView;
12 |
13 | import com.paymentkit.util.ToastUtils;
14 | import com.paymentkit.util.ViewUtils;
15 | import com.paymentkit.views.FieldHolder;
16 | import com.paymentkit.views.FieldHolder.InputStyle;
17 |
18 | /**
19 | *
20 | * @author Brendan Weinstein
21 | * http://www.brendanweinstein.me
22 | *
23 | */
24 | public class PKFragment extends Fragment {
25 |
26 | private final static String TAG = PKFragment.class.getSimpleName();
27 |
28 | public static final float INPUT_WIDTH = 0.94f; // defined in terms of screen
29 | // width
30 | private Button mSaveBtn;
31 | private ImageView mAcceptedCardsImg;
32 | private FieldHolder mFieldHolder;
33 |
34 | @Override
35 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
36 | View viewRoot = inflater.inflate(R.layout.add_credit_card, container, false);
37 | mSaveBtn = (Button) viewRoot.findViewById(R.id.save_btn);
38 | mAcceptedCardsImg = (ImageView) viewRoot.findViewById(R.id.accepted_cards);
39 | mFieldHolder = (FieldHolder) viewRoot.findViewById(R.id.field_holder);
40 | mSaveBtn.setOnClickListener(mSaveBtnListener);
41 | return viewRoot;
42 | }
43 |
44 | private OnClickListener mSaveBtnListener = new OnClickListener() {
45 | @Override
46 | public void onClick(View v) {
47 | ViewUtils.hideSoftKeyboard(getActivity());
48 | if (mFieldHolder.isFieldsValid()) {
49 | ToastUtils.showToast(getActivity(), "Valid credit card entry!");
50 | }
51 | }
52 | };
53 |
54 | private void setupViews() {
55 | float marginLeft = 1.0f - INPUT_WIDTH;
56 | ViewUtils.setMarginLeft(mAcceptedCardsImg, (int) (marginLeft * ViewUtils.getScreenWidth(getActivity())));
57 | ViewUtils.setWidth(mFieldHolder, (int) (INPUT_WIDTH * ViewUtils.getScreenWidth(getActivity())));
58 | if(Build.VERSION.SDK_INT < 14) {
59 | mFieldHolder.setInputStyle(InputStyle.GINGERBREAD);
60 | }
61 | }
62 |
63 | /* After onCreateView is called */
64 | @Override
65 | public void onActivityCreated(Bundle savedInstanceState) {
66 | super.onActivityCreated(savedInstanceState);
67 | setupViews();
68 | }
69 |
70 | }
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/drawable/edit_text_holo_light.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/pKExample/src/main/res/layout/add_credit_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
23 |
24 |
29 |
30 |
34 |
35 |
44 |
45 |
46 |
47 |
57 |
58 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/res/layout/field_holder.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
12 |
13 |
22 |
23 |
30 |
31 |
44 |
45 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/import-summary.txt:
--------------------------------------------------------------------------------
1 | ECLIPSE ANDROID PROJECT IMPORT SUMMARY
2 | ======================================
3 |
4 | Manifest Merging:
5 | -----------------
6 | Your project uses libraries that provide manifests, and your Eclipse
7 | project did not explicitly turn on manifest merging. In Android Gradle
8 | projects, manifests are always merged (meaning that contents from your
9 | libraries' manifests will be merged into the app manifest. If you had
10 | manually copied contents from library manifests into your app manifest
11 | you may need to remove these for the app to build correctly.
12 |
13 | Ignored Files:
14 | --------------
15 | The following files were *not* copied into the new Gradle project; you
16 | should evaluate whether these are still needed in your project and if
17 | so manually move them:
18 |
19 | From PKExample:
20 | * ic_launcher-web.png
21 | * proguard-project.txt
22 | From PaymentKit Droid:
23 | * .DS_Store
24 | * .gitignore
25 | * README.md
26 | * ic_launcher-web.png
27 | * proguard-project.txt
28 |
29 | Replaced Jars with Dependencies:
30 | --------------------------------
31 | The importer recognized the following .jar files as third party
32 | libraries and replaced them with Gradle dependencies instead. This has
33 | the advantage that more explicit version information is known, and the
34 | libraries can be updated automatically. However, it is possible that
35 | the .jar file in your project was of an older version than the
36 | dependency we picked, which could render the project not compileable.
37 | You can disable the jar replacement in the import wizard and try again:
38 |
39 | android-support-v4.jar => com.android.support:support-v4:+
40 |
41 | Moved Files:
42 | ------------
43 | Android Gradle projects use a different directory structure than ADT
44 | Eclipse projects. Here's how the projects were restructured:
45 |
46 | In PaymentKit Droid:
47 | * AndroidManifest.xml => paymentKitDroid/src/main/AndroidManifest.xml
48 | * libs/nineoldandroids-2.4.0.jar => paymentKitDroid/libs/nineoldandroids-2.4.0.jar
49 | * res/ => paymentKitDroid/src/main/res/
50 | * src/ => paymentKitDroid/src/main/java/
51 | In PKExample:
52 | * AndroidManifest.xml => pKExample/src/main/AndroidManifest.xml
53 | * res/ => pKExample/src/main/res/
54 | * src/ => pKExample/src/main/java/
55 |
56 | Next Steps:
57 | -----------
58 | You can now build the project. The Gradle project needs network
59 | connectivity to download dependencies.
60 |
61 | Bugs:
62 | -----
63 | If for some reason your project does not build, and you determine that
64 | it is due to a bug or limitation of the Eclipse to Gradle importer,
65 | please file a bug at http://b.android.com with category
66 | Component-Tools.
67 |
68 | (This import summary is for your information only, and can be deleted
69 | after import once you are satisfied with the results.)
70 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/util/ViewUtils.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit.util;
2 |
3 | import java.lang.reflect.InvocationTargetException;
4 | import java.lang.reflect.Method;
5 |
6 | import android.app.Activity;
7 | import android.content.Context;
8 | import android.util.Log;
9 | import android.view.View;
10 | import android.view.ViewGroup;
11 | import android.view.ViewGroup.MarginLayoutParams;
12 | import android.view.inputmethod.InputMethodManager;
13 |
14 | public class ViewUtils {
15 |
16 | private static final String TAG = ViewUtils.class.getSimpleName();
17 |
18 | private static final int LAYER_TYPE_HARDWARE = 2;
19 | private static final int LAYER_TYPE_NONE = 0;
20 | private static final int LAYER_TYPE_SOFTWARE = 1;
21 |
22 | public static void hideSoftKeyboard(final Activity activity) {
23 | if (activity.getCurrentFocus() != null) {
24 | final InputMethodManager imm = (InputMethodManager) activity.getSystemService(activity.INPUT_METHOD_SERVICE);
25 | imm.hideSoftInputFromWindow(activity.getCurrentFocus().getWindowToken(), 0);
26 | }
27 | }
28 |
29 | public static int getScreenHeight(final Context context) {
30 | return context.getResources().getDisplayMetrics().heightPixels;
31 | }
32 |
33 | public static int getScreenWidth(final Context context) {
34 | return context.getResources().getDisplayMetrics().widthPixels;
35 | }
36 |
37 | public static void moveVertical(final View v, final float distance) {
38 | final MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
39 | final int newTopMargin = (int) (lp.topMargin + distance);
40 | setMarginTop(v, newTopMargin);
41 | }
42 |
43 | public static void setMarginTop(final View v, final int margin) {
44 | final MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
45 | lp.setMargins(lp.leftMargin, margin, lp.rightMargin, lp.bottomMargin);
46 | v.setLayoutParams(lp);
47 | }
48 |
49 | public static void setMarginBottom(final View v, final int margin) {
50 | final MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
51 | lp.setMargins(lp.leftMargin, lp.topMargin, lp.rightMargin, margin);
52 | v.setLayoutParams(lp);
53 | }
54 |
55 | public static void setMarginLeft(final View v, final int margin) {
56 | final MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
57 | lp.setMargins(margin, lp.topMargin, lp.rightMargin, lp.bottomMargin);
58 | v.setLayoutParams(lp);
59 | }
60 |
61 | public static void setMarginRight(final View v, final int margin) {
62 | final MarginLayoutParams lp = (MarginLayoutParams) v.getLayoutParams();
63 | lp.setMargins(lp.leftMargin, lp.topMargin, margin, lp.bottomMargin);
64 | v.setLayoutParams(lp);
65 | }
66 |
67 | public static void setHeight(final View v, final int height) {
68 | final ViewGroup.LayoutParams lp = v.getLayoutParams();
69 | if (lp.height != height) {
70 | lp.height = height;
71 | v.setLayoutParams(lp);
72 | }
73 | }
74 |
75 | public static void setWidth(final View v, final int width) {
76 | final ViewGroup.LayoutParams lp = v.getLayoutParams();
77 | if (lp.width != width) {
78 | lp.width = width;
79 | v.setLayoutParams(lp);
80 | }
81 | }
82 |
83 | public static void setHardwareLayer(View view) {
84 | setLayerType(view, LAYER_TYPE_HARDWARE);
85 | }
86 |
87 | public static void setLayerTypeNone(View view) {
88 | setLayerType(view, LAYER_TYPE_NONE);
89 | }
90 |
91 | public static void setLayerTypeSoftware(View view) {
92 | setLayerType(view, LAYER_TYPE_SOFTWARE);
93 | }
94 |
95 | public static void setLayerType(View view, int type) {
96 | try {
97 | Class[] paramTypes = new Class[1];
98 | paramTypes[0] = Integer.TYPE;
99 | Method setLayerTypeMethod = view.getClass().getMethod("setLayerType", paramTypes);
100 | setLayerTypeMethod.invoke(view, type);
101 | Log.d(TAG, "setLayerType successfully called");
102 | } catch (NoSuchMethodException e) {
103 | // method does not exist in this api level
104 | } catch (IllegalArgumentException e) {
105 | e.printStackTrace();
106 | } catch (IllegalAccessException e) {
107 | e.printStackTrace();
108 | } catch (InvocationTargetException e) {
109 | e.printStackTrace();
110 | }
111 | }
112 |
113 | }
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/views/CVVEditText.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit.views;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.text.Editable;
6 | import android.text.TextWatcher;
7 | import android.util.AttributeSet;
8 | import android.view.KeyEvent;
9 | import android.view.View;
10 | import android.view.inputmethod.EditorInfo;
11 | import android.view.inputmethod.InputConnection;
12 | import android.view.inputmethod.InputConnectionWrapper;
13 | import android.widget.EditText;
14 | import android.widget.TextView;
15 |
16 | import com.paymentkit.util.ViewUtils;
17 | import com.paymentkit.views.FieldHolder.CardEntryListener;
18 |
19 | public class CVVEditText extends EditText {
20 |
21 | private static final String TAG = CVVEditText.class.getSimpleName();
22 |
23 | private CardEntryListener mListener;
24 |
25 | public CVVEditText(Context context, AttributeSet attrs) {
26 | super(context, attrs);
27 | setup();
28 | }
29 |
30 | public CVVEditText(Context context) {
31 | super(context);
32 | setup();
33 | }
34 |
35 | private void setup() {
36 | addTextChangedListener(mTextWatcher);
37 | setOnFocusChangeListener(mFocusListener);
38 | //setOnEditorActionListener(mEditorActionListener);
39 | }
40 |
41 | public void setCardEntryListener(CardEntryListener listener) {
42 | mListener = listener;
43 | }
44 |
45 | private OnEditorActionListener mEditorActionListener = new EditText.OnEditorActionListener() {
46 | @Override
47 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
48 | if (actionId == EditorInfo.IME_ACTION_DONE ||
49 | event.getAction() == KeyEvent.ACTION_DOWN &&
50 | event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
51 | clearFocus();
52 | ViewUtils.hideSoftKeyboard((Activity)getContext());
53 | return true;
54 | }
55 | return false;
56 | }
57 | };
58 |
59 | private OnFocusChangeListener mFocusListener = new OnFocusChangeListener() {
60 | @Override
61 | public void onFocusChange(View v, boolean hasFocus) {
62 | if(hasFocus) {
63 | mListener.onCVVEntry();
64 | } else {
65 | mListener.onCVVEntryComplete();
66 | }
67 | }
68 | };
69 |
70 | @Override
71 | public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
72 | return new ZanyInputConnection(super.onCreateInputConnection(outAttrs),
73 | true);
74 | }
75 |
76 | /*
77 | * See "android EditText delete(backspace) key event" on stackoverflow
78 | */
79 | private class ZanyInputConnection extends InputConnectionWrapper {
80 |
81 | public ZanyInputConnection(InputConnection target, boolean mutable) {
82 | super(target, mutable);
83 | }
84 |
85 | @Override
86 | public boolean sendKeyEvent(KeyEvent event) {
87 | if (event.getAction() == KeyEvent.ACTION_DOWN
88 | && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
89 | if(getSelectionStart() == 0) {
90 | mListener.onBackFromCVV();
91 | return false;
92 | }
93 | }
94 | return super.sendKeyEvent(event);
95 | }
96 |
97 | @Override
98 | public boolean deleteSurroundingText(int beforeLength, int afterLength) {
99 | // magic: in latest Android, deleteSurroundingText(1, 0) will be
100 | // called for backspace
101 | if (beforeLength == 1 && afterLength == 0) {
102 | // backspace
103 | return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN,
104 | KeyEvent.KEYCODE_DEL))
105 | && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP,
106 | KeyEvent.KEYCODE_DEL));
107 | }
108 |
109 | return super.deleteSurroundingText(beforeLength, afterLength);
110 | }
111 | }
112 |
113 | private TextWatcher mTextWatcher = new TextWatcher() {
114 | @Override
115 | public void afterTextChanged(Editable s) {
116 | if (s.length() == FieldHolder.CVV_MAX_LENGTH) {
117 | //mListener.onCVVEntryComplete();
118 | ViewUtils.hideSoftKeyboard((Activity)getContext());
119 | clearFocus();
120 | }
121 | }
122 |
123 | @Override
124 | public void beforeTextChanged(CharSequence s, int start, int count,
125 | int after) {
126 | }
127 |
128 | @Override
129 | public void onTextChanged(CharSequence s, int start, int before,
130 | int count) {
131 | }
132 | };
133 |
134 | }
135 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/views/CardIcon.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit.views;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.FrameLayout;
7 | import android.widget.ImageView;
8 | import android.widget.ImageView.ScaleType;
9 |
10 | import com.nineoldandroids.animation.Animator;
11 | import com.nineoldandroids.animation.AnimatorListenerAdapter;
12 | import com.nineoldandroids.animation.ObjectAnimator;
13 | import com.paymentkit.CardType;
14 | import com.paymentkit.R;
15 | import com.paymentkit.util.ViewUtils;
16 |
17 |
18 | public class CardIcon extends FrameLayout {
19 |
20 | private static final String TAG = CardIcon.class.getSimpleName();
21 | private static final Long HALF_FLIP_DURATION = 125L;
22 |
23 | private CardType mCardType = CardType.UNKNOWN_CARD;
24 | private CardFace mCardFace = CardFace.FRONT;
25 |
26 | private ImageView mFrontFace;
27 | private ImageView mBackFace;
28 |
29 | public CardIcon(Context context, AttributeSet attrs) {
30 | super(context, attrs);
31 | setup();
32 | setCardType(CardType.UNKNOWN_CARD);
33 | }
34 |
35 | private void setup() {
36 | mFrontFace = new ImageView(getContext());
37 | mBackFace = new ImageView(getContext());
38 |
39 | mFrontFace.setScaleType(ScaleType.CENTER_INSIDE);
40 | mBackFace.setScaleType(ScaleType.CENTER_INSIDE);
41 |
42 | FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
43 | addView(mBackFace, lp);
44 | addView(mFrontFace, lp);
45 |
46 | mBackFace.setImageDrawable(getContext().getResources().getDrawable(R.drawable.checkout_card_cvc));
47 | alphaOut(mBackFace);
48 | }
49 |
50 | public void setCardType(CardType cardType) {
51 | mCardType = cardType;
52 | switch (cardType) {
53 | case VISA:
54 | mFrontFace.setImageDrawable(getContext().getResources().getDrawable(R.drawable.checkout_card_visa));
55 | break;
56 | case AMERICAN_EXPRESS:
57 | mFrontFace.setImageDrawable(getContext().getResources().getDrawable(R.drawable.checkout_card_amex));
58 | break;
59 | case DISCOVER:
60 | mFrontFace.setImageDrawable(getContext().getResources().getDrawable(R.drawable.checkout_card_discover));
61 | break;
62 | case MASTERCARD:
63 | mFrontFace.setImageDrawable(getContext().getResources().getDrawable(R.drawable.checkout_card_master));
64 | break;
65 | default:
66 | mFrontFace.setImageDrawable(getContext().getResources().getDrawable(R.drawable.checkout_default_card));
67 | }
68 | }
69 |
70 | public enum CardFace {
71 | BACK, FRONT
72 | };
73 |
74 | public void flipTo(CardFace face) {
75 | if (mCardFace == face) {
76 | return;
77 | }
78 |
79 | if (face == CardFace.BACK) {
80 | mCardFace = face;
81 | flipToBack();
82 | } else {
83 | mCardFace = face;
84 | flipToFront();
85 | }
86 | }
87 |
88 | private void flipToBack() {
89 | ViewUtils.setHardwareLayer(mFrontFace);
90 | ObjectAnimator rotateFront = ObjectAnimator.ofFloat(mFrontFace, "scaleX", 1.0f, 0.0f);
91 | rotateFront.setDuration(HALF_FLIP_DURATION);
92 |
93 | ViewUtils.setHardwareLayer(mBackFace);
94 | final ObjectAnimator rotateBack = ObjectAnimator.ofFloat(mBackFace, "scaleX", 0.0f, 1.0f);
95 | rotateBack.setDuration(HALF_FLIP_DURATION);
96 | rotateBack.addListener(new AnimatorListenerAdapter() {
97 | @Override
98 | public void onAnimationEnd(Animator anim) {
99 | ViewUtils.setLayerTypeNone(mBackFace);
100 | }
101 | });
102 |
103 | rotateFront.addListener(new AnimatorListenerAdapter() {
104 | @Override
105 | public void onAnimationEnd(Animator anim) {
106 | alphaOut(mFrontFace);
107 | ViewUtils.setLayerTypeNone(mFrontFace);
108 | alphaIn(mBackFace);
109 | rotateBack.start();
110 | }
111 | });
112 |
113 | rotateFront.start();
114 | }
115 |
116 | private void flipToFront() {
117 | ViewUtils.setHardwareLayer(mBackFace);
118 | ObjectAnimator rotateBackFace = ObjectAnimator.ofFloat(mBackFace, "scaleX", 1.0f, 0.0f);
119 | rotateBackFace.setDuration(HALF_FLIP_DURATION);
120 |
121 | ViewUtils.setLayerTypeNone(mFrontFace);
122 | final ObjectAnimator rotateFrontFace = ObjectAnimator.ofFloat(mFrontFace, "scaleX", 0.0f, 1.0f);
123 | rotateFrontFace.setDuration(HALF_FLIP_DURATION);
124 | rotateFrontFace.addListener(new AnimatorListenerAdapter() {
125 | @Override
126 | public void onAnimationEnd(Animator anim) {
127 | ViewUtils.setLayerTypeNone(mBackFace);
128 | }
129 | });
130 |
131 | rotateBackFace.addListener(new AnimatorListenerAdapter() {
132 | @Override
133 | public void onAnimationEnd(Animator anim) {
134 | alphaOut(mBackFace);
135 | ViewUtils.setLayerTypeNone(mBackFace);
136 | alphaIn(mFrontFace);
137 | rotateFrontFace.start();
138 | }
139 | });
140 |
141 | rotateBackFace.start();
142 | }
143 |
144 | private void alphaOut(View v) {
145 | ObjectAnimator alphaOut = ObjectAnimator.ofFloat(v, "alpha", 0.0f).setDuration(0);
146 | alphaOut.start();
147 | }
148 |
149 | private void alphaIn(View v) {
150 | ObjectAnimator alphaIn = ObjectAnimator.ofFloat(v, "alpha", 1.0f).setDuration(0);
151 | alphaIn.start();
152 | }
153 |
154 | public CardType getCardType() {
155 | return mCardType;
156 | }
157 |
158 | }
159 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/views/CardNumHolder.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit.views;
2 |
3 | import android.content.Context;
4 | import android.graphics.Color;
5 | import android.graphics.Paint;
6 | import android.util.AttributeSet;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.animation.CycleInterpolator;
10 | import android.widget.RelativeLayout;
11 |
12 | import com.nineoldandroids.animation.Animator;
13 | import com.nineoldandroids.animation.AnimatorListenerAdapter;
14 | import com.nineoldandroids.animation.ObjectAnimator;
15 | import com.paymentkit.R;
16 | import com.paymentkit.ValidateCreditCard;
17 | import com.paymentkit.util.ToastUtils;
18 | import com.paymentkit.util.ViewUtils;
19 | import com.paymentkit.views.FieldHolder.CardEntryListener;
20 |
21 | public class CardNumHolder extends RelativeLayout {
22 |
23 | private static final String TAG = CardNumHolder.class.getSimpleName();
24 |
25 | private static final int SHAKE_DURATION = 400;
26 |
27 | private CardNumEditText mCardNumberEditText;
28 | private InterceptEditText mLastFourDigits;
29 | private float mLeftOffset;
30 | private CardEntryListener mCardEntryListener;
31 | private View mTopItem;
32 | private int mSwitchIndex = 0;
33 |
34 | public CardNumHolder(Context context) {
35 | super(context);
36 | setup();
37 | }
38 |
39 | public CardNumHolder(Context context, AttributeSet attrs) {
40 | super(context, attrs);
41 | setup();
42 | }
43 |
44 | public String getCardNumberEditText() {
45 | return mCardNumberEditText.toString();
46 | }
47 |
48 | public void setCardNumberEditText(String mCardNumberEditText) {
49 | this.mCardNumberEditText.setText(mCardNumberEditText);
50 | }
51 |
52 | private void setup() {
53 | setClipChildren(false);
54 | LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
55 | inflater.inflate(R.layout.card_holder, this, true);
56 | mCardNumberEditText = (CardNumEditText) findViewById(R.id.credit_card_no);
57 | mLastFourDigits = (InterceptEditText) findViewById(R.id.last_four_digits);
58 | mTopItem = mLastFourDigits;
59 | }
60 |
61 | public boolean isCardNumValid() {
62 | if (mCardNumberEditText.length() < mCardNumberEditText.getMaxCardLength()) {
63 | ToastUtils.showToast(getContext(), getResources().getString(R.string.error_invalid_card_no));
64 | return false;
65 | } else if (mCardNumberEditText.length() == mCardNumberEditText.getMaxCardLength()) {
66 | if (ValidateCreditCard.isValid(Long.parseLong(getCardField().getText().toString().replaceAll("\\s", "")))) {
67 | return true;
68 | } else {
69 | ToastUtils.showToast(getContext(), getResources().getString(R.string.error_invalid_card_no));
70 | }
71 | }
72 | return false;
73 | }
74 |
75 | private boolean mIsClickable = true;
76 |
77 | public void setIsClickable(boolean val) {
78 | mIsClickable = val;
79 | }
80 |
81 | public void setCardEntryListener(CardEntryListener listener) {
82 | mCardEntryListener = listener;
83 | mCardNumberEditText.setCardEntryListener(listener);
84 | mLastFourDigits.setOnClickListener(new OnClickListener() {
85 | @Override
86 | public void onClick(View v) {
87 | if (mIsClickable) {
88 | mCardEntryListener.onCardNumberInputReEntry();
89 | }
90 | }
91 | });
92 | }
93 |
94 | @Override
95 | protected int getChildDrawingOrder(int childCount, int i) {
96 | View child = getChildAt(i);
97 | if (child instanceof CardNumEditText && mCardNumberEditText == mTopItem) {
98 | mSwitchIndex = i;
99 | return this.getChildCount() - 1;
100 | } else if (!(child instanceof CardNumEditText) && mTopItem == mCardNumberEditText) {
101 | return mSwitchIndex;
102 | }
103 | return i;
104 | }
105 |
106 | public void indicateInvalidCardNum() {
107 | getCardField().setTextColor(Color.RED);
108 | mTopItem = mCardNumberEditText;
109 | ObjectAnimator shakeAnim = ObjectAnimator.ofFloat(getCardField(), "translationX", -16);
110 | shakeAnim.setDuration(SHAKE_DURATION);
111 | shakeAnim.setInterpolator(new CycleInterpolator(2.0f));
112 | shakeAnim.addListener(new AnimatorListenerAdapter() {
113 | @Override
114 | public void onAnimationEnd(Animator anim) {
115 | mTopItem = null;
116 | }
117 | });
118 | shakeAnim.start();
119 | }
120 |
121 | public CardNumEditText getCardField() {
122 | return mCardNumberEditText;
123 | }
124 |
125 | public void createOverlay() {
126 | // create 4 digits
127 | String str = mCardNumberEditText.getText().toString();
128 | String last4Digits = str.substring(str.length() - 4, str.length());
129 | mLastFourDigits.setText(last4Digits);
130 | Paint textPaint = mCardNumberEditText.getPaint();
131 | float fullWidth = textPaint.measureText(mCardNumberEditText.getText().toString());
132 | float fourDigitsWidth = textPaint.measureText(last4Digits);
133 | mLeftOffset = fullWidth - fourDigitsWidth;
134 | ViewUtils.setMarginLeft(mLastFourDigits, (int) mLeftOffset);
135 | // align digits on right
136 | mLastFourDigits.setTextColor(Color.DKGRAY);
137 | mLastFourDigits.setVisibility(View.VISIBLE);
138 | }
139 |
140 | public void destroyOverlay() {
141 | mLastFourDigits.setVisibility(View.GONE);
142 | }
143 |
144 | public float getLeftOffset() {
145 | return mLeftOffset;
146 | }
147 |
148 | }
149 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/views/ExpirationEditText.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit.views;
2 |
3 | import java.util.Calendar;
4 |
5 | import android.content.Context;
6 | import android.text.Editable;
7 | import android.text.TextWatcher;
8 | import android.util.AttributeSet;
9 | import android.view.KeyEvent;
10 | import android.view.inputmethod.EditorInfo;
11 | import android.view.inputmethod.InputConnection;
12 | import android.view.inputmethod.InputConnectionWrapper;
13 | import android.widget.EditText;
14 |
15 | import com.paymentkit.views.FieldHolder.CardEntryListener;
16 |
17 | /**
18 | *
19 | * @author Brendan Weinstein
20 | * http://www.brendanweinstein.me
21 | *
22 | */
23 | public class ExpirationEditText extends EditText {
24 |
25 | private static final String TAG = ExpirationEditText.class.getSimpleName();
26 |
27 | private CardEntryListener mListener;
28 |
29 | public ExpirationEditText(Context context) {
30 | super(context);
31 | setup();
32 | }
33 |
34 | public ExpirationEditText(Context context, AttributeSet attrs) {
35 | super(context, attrs);
36 | setup();
37 | }
38 |
39 | private void setup() {
40 | addTextChangedListener(mTextWatcher);
41 | }
42 |
43 | @Override
44 | public void onSelectionChanged(int start, int end) {
45 | CharSequence text = getText();
46 | if (text != null) {
47 | if (start != text.length() || end != text.length()) {
48 | setSelection(text.length(), text.length());
49 | return;
50 | }
51 | }
52 |
53 | super.onSelectionChanged(start, end);
54 | }
55 |
56 | public void setCardEntryListener(CardEntryListener listener) {
57 | mListener = listener;
58 | }
59 |
60 | /* Expiration Input Field Text Watcher */
61 | private boolean mTextAdded = true; // denotes if a character was added or
62 | // removed in last text change
63 | private int mPrevLength = 0;
64 |
65 | TextWatcher mTextWatcher = new TextWatcher() {
66 |
67 | @Override
68 | public void afterTextChanged(Editable s) {
69 | if (mTextAdded && length() == 1) {
70 |
71 | // tens place of month validation
72 | int tensPlace = Integer.parseInt(getText().subSequence(0, 1).toString());
73 | if (tensPlace > 1) {
74 | setText("");
75 | setSelection(length());
76 | }
77 |
78 | } else if (mTextAdded && length() == 2) {
79 |
80 | // one places of month validation
81 | int tensPlace = Integer.parseInt(getText().subSequence(0, 1).toString());
82 | int onesPlace = Integer.parseInt(getText().subSequence(1, 2).toString());
83 | if ((tensPlace == 1 && onesPlace > 2) || (tensPlace == 0 && onesPlace == 0)) {
84 | setText(getText().subSequence(0, 1));
85 | setSelection(length());
86 | } else {
87 | setText(getText().subSequence(0, 2) + "/");
88 | setSelection(length());
89 | }
90 |
91 | } else if (mTextAdded && length() == 4) {
92 |
93 | // tens place of year validation
94 | Integer year = Calendar.getInstance().get(Calendar.YEAR); // 2013
95 | int curTensPlace = Integer.parseInt(year.toString().substring(2, 3)); // 1
96 | int inputTensPlace = Integer.parseInt(Character.toString(getText().charAt(3))); // 5
97 |
98 | if (inputTensPlace < curTensPlace) {
99 | setText(getText().subSequence(0, length() - 1));
100 | setSelection(length());
101 | }
102 |
103 | } else if (mTextAdded && length() == 5) {
104 |
105 | // ones place of year validation
106 | Integer year = Calendar.getInstance().get(Calendar.YEAR);
107 | int curMonth = Calendar.getInstance().get(Calendar.MONTH);
108 | int curYear = Integer.parseInt(year.toString().substring(2, 4));
109 | int inputMonth = Integer.parseInt(getText().toString().substring(0, 2));
110 | int inputYear = Integer.parseInt(getText().toString().substring(3, 5));
111 | if (inputYear < curYear || ((inputMonth < curMonth) && inputYear == curYear)) {
112 | setText(getText().subSequence(0, 4));
113 | setSelection(length());
114 | } else {
115 | mListener.onCVVEntry();
116 | }
117 |
118 | } else if (!mTextAdded && (length() == 2)) {
119 |
120 | setText(getText().subSequence(0, 1));
121 | setSelection(length());
122 |
123 | }
124 | }
125 |
126 | @Override
127 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
128 | mPrevLength = length();
129 | }
130 |
131 | @Override
132 | public void onTextChanged(CharSequence s, int start, int before, int count) {
133 | if (length() - mPrevLength > 0) {
134 | mTextAdded = true;
135 | } else {
136 | mTextAdded = false;
137 | }
138 | }
139 | };
140 |
141 | public String getMonth() {
142 | String text = getText().toString();
143 | int index = text.indexOf("/");
144 | return text.substring(0, index);
145 | }
146 |
147 | public String getYear() {
148 | String text = getText().toString();
149 | int index = text.indexOf("/");
150 | String yearStr = "20" + text.substring(index + 1, text.length());
151 | return yearStr;
152 | }
153 |
154 | @Override
155 | public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
156 | return new ZanyInputConnection(super.onCreateInputConnection(outAttrs), true);
157 | }
158 |
159 | /*
160 | * See "android EditText delete(backspace) key event" on stackoverflow
161 | */
162 | private class ZanyInputConnection extends InputConnectionWrapper {
163 |
164 | public ZanyInputConnection(InputConnection target, boolean mutable) {
165 | super(target, mutable);
166 | }
167 |
168 | @Override
169 | public boolean sendKeyEvent(KeyEvent event) {
170 | if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
171 | if (getSelectionStart() == 0) {
172 | mListener.onCardNumberInputReEntry();
173 | }
174 | }
175 | return super.sendKeyEvent(event);
176 | }
177 |
178 | @Override
179 | public boolean deleteSurroundingText(int beforeLength, int afterLength) {
180 | // magic: in latest Android, deleteSurroundingText(1, 0) will be
181 | // called for backspace
182 | if (beforeLength == 1 && afterLength == 0) {
183 | // backspace
184 | return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
185 | && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
186 | }
187 |
188 | return super.deleteSurroundingText(beforeLength, afterLength);
189 | }
190 | }
191 |
192 | }
193 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/views/CardNumEditText.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit.views;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.Color;
6 | import android.text.Editable;
7 | import android.text.InputFilter;
8 | import android.text.TextWatcher;
9 | import android.util.AttributeSet;
10 | import android.util.Log;
11 | import android.view.KeyEvent;
12 | import android.view.inputmethod.EditorInfo;
13 | import android.view.inputmethod.InputConnection;
14 | import android.view.inputmethod.InputConnectionWrapper;
15 | import android.widget.EditText;
16 |
17 | import com.paymentkit.views.FieldHolder.CardEntryListener;
18 |
19 | public class CardNumEditText extends EditText {
20 |
21 | private static final String TAG = CardNumEditText.class.getSimpleName();
22 |
23 | private CardEntryListener mCardEntryListener;
24 |
25 | private int mMaxCardLength = FieldHolder.NON_AMEX_CARD_LENGTH;
26 |
27 | public CardNumEditText(Context context) {
28 | super(context);
29 | setup();
30 | }
31 |
32 | public CardNumEditText(Context context, AttributeSet attrs) {
33 | super(context, attrs);
34 | setup();
35 | }
36 |
37 | private void setup() {
38 | addTextChangedListener(mCardNumberTextWatcher);
39 | }
40 |
41 | public void setCardEntryListener(CardEntryListener listener) {
42 | mCardEntryListener = listener;
43 | }
44 |
45 | public int getMaxCardLength() {
46 | return mMaxCardLength;
47 | }
48 |
49 | public void setMaxCardLength(int maxCardLength) {
50 | mMaxCardLength = maxCardLength;
51 | InputFilter[] filters = new InputFilter[1];
52 | filters[0] = new InputFilter.LengthFilter(maxCardLength);
53 | setFilters(filters);
54 | }
55 |
56 | /* Card Number Input Field Text Watcher */
57 | private boolean mTextAdded = true;
58 | private int mPrevLength = 0;
59 |
60 | enum TextEvent {
61 | KEY_PRESS, FORMATTER
62 | };
63 |
64 | private TextEvent mLastEvent = TextEvent.KEY_PRESS;
65 |
66 | @Override
67 | public void onDraw(Canvas canvas) {
68 | super.onDraw(canvas);
69 | }
70 |
71 | TextWatcher mCardNumberTextWatcher = new TextWatcher() {
72 | @Override
73 | public void afterTextChanged(Editable s) {
74 | setTextColor(Color.DKGRAY);
75 | mCardEntryListener.onEdit();
76 | if (length() == mMaxCardLength && mTextAdded) {
77 | mCardEntryListener.onCardNumberInputComplete();
78 | }
79 | if (mLastEvent == TextEvent.KEY_PRESS) {
80 | int curPos = getSelectionEnd();
81 | formatText(getText().toString().replaceAll(" ", ""));
82 | positionCursor(curPos);
83 | }
84 | if (mLastEvent != TextEvent.KEY_PRESS) {
85 | mLastEvent = TextEvent.KEY_PRESS;
86 | }
87 | }
88 |
89 | private void formatText(String strippedStr) {
90 | if(mMaxCardLength == FieldHolder.NON_AMEX_CARD_LENGTH) {
91 | format16Text(strippedStr);
92 | } else if(mMaxCardLength == FieldHolder.AMEX_CARD_LENGTH) {
93 | format15Text(strippedStr);
94 | }
95 | }
96 |
97 | private void positionCursor(int curPos) {
98 | if (mMaxCardLength == FieldHolder.NON_AMEX_CARD_LENGTH) {
99 | if (mTextAdded && (curPos == 4 || curPos == 9 || curPos == 14)) {
100 | setSelection(curPos + 1);
101 | } else {
102 | setSelection(curPos);
103 | }
104 | } else if(mMaxCardLength == FieldHolder.AMEX_CARD_LENGTH) {
105 | if (mTextAdded && (curPos == 4 || curPos == 11)) {
106 | setSelection(curPos + 1);
107 | } else {
108 | setSelection(curPos);
109 | }
110 | }
111 | }
112 |
113 | @Override
114 | public void beforeTextChanged(CharSequence s, int start, int count, int after) {
115 | mPrevLength = length();
116 | }
117 |
118 | @Override
119 | public void onTextChanged(CharSequence s, int start, int before, int count) {
120 | if (length() - mPrevLength > 0) {
121 | mTextAdded = true;
122 | } else {
123 | mTextAdded = false;
124 | }
125 | }
126 | };
127 |
128 | /*
129 | * 4-4-4-4
130 | */
131 | private void format16Text(String strippedStr) {
132 | int len = strippedStr.length();
133 | StringBuilder sb = new StringBuilder();
134 | for (int lh = 1; lh <= len; lh++) {
135 | sb.append(strippedStr.charAt(lh - 1));
136 | if (lh % 4 == 0 && lh < 16) {
137 | sb.append(" ");
138 | }
139 | }
140 | mLastEvent = TextEvent.FORMATTER;
141 | setText(sb.toString());
142 | }
143 |
144 | /*
145 | * 4-6-5
146 | */
147 | private void format15Text(String strippedStr) {
148 | int len = strippedStr.length();
149 | StringBuilder sb = new StringBuilder();
150 | for(int lh=1; lh <= len; lh++) {
151 | sb.append(strippedStr.charAt(lh-1));
152 | if(lh == 4 || lh == 10) {
153 | sb.append(" ");
154 | }
155 | }
156 | mLastEvent = TextEvent.FORMATTER;
157 | setText(sb.toString());
158 | }
159 |
160 | @Override
161 | public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
162 | return new ZanyInputConnection(super.onCreateInputConnection(outAttrs), true);
163 | }
164 |
165 | private class ZanyInputConnection extends InputConnectionWrapper {
166 |
167 | public ZanyInputConnection(InputConnection target, boolean mutable) {
168 | super(target, mutable);
169 | }
170 |
171 | @Override
172 | public boolean sendKeyEvent(KeyEvent event) {
173 | if (event.getAction() == KeyEvent.ACTION_DOWN && event.getKeyCode() == KeyEvent.KEYCODE_DEL) {
174 | int curPos = getSelectionEnd();
175 | mLastEvent = TextEvent.KEY_PRESS;
176 | if (mMaxCardLength == FieldHolder.NON_AMEX_CARD_LENGTH && (curPos == 5 || curPos == 10 || curPos == 15)) {
177 | CardNumEditText.this.setSelection(curPos - 1);
178 | return true;
179 | } else if(mMaxCardLength == FieldHolder.AMEX_CARD_LENGTH && (curPos == 5 || curPos == 12)) {
180 | CardNumEditText.this.setSelection(curPos - 1);
181 | return true;
182 | }
183 | }
184 | return super.sendKeyEvent(event);
185 | }
186 |
187 | @Override
188 | public boolean deleteSurroundingText(int beforeLength, int afterLength) {
189 | // magic: in latest Android, deleteSurroundingText(1, 0) will be
190 | // called for backspace
191 | if (beforeLength == 1 && afterLength == 0) {
192 | // backspace
193 | return sendKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL))
194 | && sendKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DEL));
195 | }
196 |
197 | return super.deleteSurroundingText(beforeLength, afterLength);
198 | }
199 | }
200 |
201 | public String getLast4Digits() {
202 | String text = getText().toString().replaceAll("\\s", "");
203 | return text.substring(text.length() - 4, text.length());
204 | }
205 |
206 | }
207 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/views/FieldHolder.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit.views;
2 |
3 | import android.content.Context;
4 | import android.text.InputFilter;
5 | import android.util.AttributeSet;
6 | import android.view.LayoutInflater;
7 | import android.view.View;
8 | import android.view.animation.OvershootInterpolator;
9 | import android.widget.LinearLayout;
10 | import android.widget.RelativeLayout;
11 |
12 | import com.nineoldandroids.animation.Animator;
13 | import com.nineoldandroids.animation.AnimatorListenerAdapter;
14 | import com.nineoldandroids.animation.AnimatorSet;
15 | import com.nineoldandroids.animation.ObjectAnimator;
16 | import com.paymentkit.CardType;
17 | import com.paymentkit.R;
18 | import com.paymentkit.ValidateCreditCard;
19 | import com.paymentkit.util.ViewUtils;
20 | import com.paymentkit.views.CardIcon.CardFace;
21 |
22 | /**
23 | *
24 | * @author Brendan Weinstein
25 | *
26 | */
27 | public class FieldHolder extends RelativeLayout {
28 |
29 | public static int CVV_MAX_LENGTH = 3;
30 |
31 | protected static final int AMEX_CARD_LENGTH = 17;
32 | public static final int NON_AMEX_CARD_LENGTH = 19;
33 |
34 | private static final int RE_ENTRY_ALPHA_OUT_DURATION = 100;
35 | private static final int RE_ENTRY_ALPHA_IN_DURATION = 500;
36 | private static final int RE_ENTRY_OVERSHOOT_DURATION = 500;
37 |
38 | private CardNumHolder mCardHolder;
39 | private ExpirationEditText mExpirationEditText;
40 | private CVVEditText mCVVEditText;
41 | private CardIcon mCardIcon;
42 | private LinearLayout mExtraFields;
43 |
44 | public enum InputStyle {
45 | GINGERBREAD(R.drawable.edit_text), ICS_HOLO_LIGHT(R.drawable.edit_text_holo_light);
46 |
47 | int resId;
48 |
49 | InputStyle(int resId) {
50 | this.resId = resId;
51 | }
52 |
53 | int getResId() {
54 | return resId;
55 | }
56 |
57 | };
58 | private InputStyle mInputStyle = InputStyle.ICS_HOLO_LIGHT;
59 |
60 | public FieldHolder(Context context) {
61 | super(context);
62 | setup();
63 | }
64 |
65 | public FieldHolder(Context context, AttributeSet attrs) {
66 | super(context, attrs);
67 | setup();
68 | }
69 |
70 | public String getCardNumber() {
71 | if (this.mCardHolder.getCardField().getText() != null) {
72 | return this.mCardHolder.getCardField().getText().toString();
73 | }
74 | return null;
75 | }
76 |
77 | public String getCardExpirationMonth() {
78 | return this.mExpirationEditText.getMonth();
79 | }
80 |
81 | public String getCardExpirationYear() {
82 | return this.mExpirationEditText.getYear();
83 | }
84 |
85 | public String getCardCVV() {
86 | if (this.mCVVEditText.getText() != null) {
87 | return this.mCVVEditText.getText().toString();
88 | }
89 | return null;
90 | }
91 |
92 | /*
93 | * Determines background style of the FieldHolder
94 | */
95 | public void setInputStyle(InputStyle inputStyle) {
96 | mInputStyle = inputStyle;
97 | setNecessaryFields();
98 | }
99 |
100 | private void setup() {
101 | LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
102 | inflater.inflate(R.layout.field_holder, this, true);
103 | mCardHolder = (CardNumHolder) findViewById(R.id.card_num_holder);
104 | mCardIcon = (CardIcon) findViewById(R.id.card_icon);
105 | mExtraFields = (LinearLayout) findViewById(R.id.extra_fields);
106 | mExpirationEditText = (ExpirationEditText) findViewById(R.id.expiration);
107 | mCVVEditText = (CVVEditText) findViewById(R.id.security_code);
108 | mCardHolder.setCardEntryListener(mCardEntryListener);
109 | setupViews();
110 | }
111 |
112 | private void setupViews() {
113 | setExtraFieldsAlpha();
114 | setCardEntryListeners();
115 | setNecessaryFields();
116 | }
117 |
118 | private void setNecessaryFields() {
119 | setFocusable(true);
120 | setFocusableInTouchMode(true);
121 | setClipChildren(false);
122 | setBackgroundDrawable(getResources().getDrawable(mInputStyle.getResId()));
123 | }
124 |
125 | private void setExtraFieldsAlpha() {
126 | ObjectAnimator setAlphaZero = ObjectAnimator.ofFloat(mExtraFields, "alpha", 0.0f);
127 | setAlphaZero.setDuration(0);
128 | setAlphaZero.start();
129 | mExtraFields.setVisibility(View.GONE);
130 | }
131 |
132 | private void setCardEntryListeners() {
133 | mExpirationEditText.setCardEntryListener(mCardEntryListener);
134 | mCVVEditText.setCardEntryListener(mCardEntryListener);
135 | }
136 |
137 | private void validateCard() {
138 | long cardNumber = Long.parseLong(mCardHolder.getCardField().getText().toString().replaceAll("\\s", ""));
139 | if (ValidateCreditCard.isValid(cardNumber)) {
140 | CardType cardType = ValidateCreditCard.matchCardType(cardNumber);
141 | mCardIcon.setCardType(cardType);
142 | transitionToExtraFields();
143 | } else {
144 | mCardHolder.indicateInvalidCardNum();
145 | }
146 | }
147 |
148 | private void transitionToExtraFields() {
149 | // CREATE LAST 4 DIGITS OVERLAY
150 | mCardHolder.createOverlay();
151 |
152 | // MOVE CARD NUMBER TO LEFT AND ALPHA OUT
153 | AnimatorSet set = new AnimatorSet();
154 | ViewUtils.setHardwareLayer(mCardHolder);
155 | ObjectAnimator translateAnim = ObjectAnimator.ofFloat(mCardHolder, "translationX", -mCardHolder.getLeftOffset());
156 | translateAnim.setDuration(500);
157 |
158 | ObjectAnimator alphaOut = ObjectAnimator.ofFloat(mCardHolder.getCardField(), "alpha", 0.0f);
159 | alphaOut.setDuration(500);
160 | alphaOut.addListener(new AnimatorListenerAdapter() {
161 | @Override
162 | public void onAnimationEnd(Animator anim) {
163 | mCardHolder.getCardField().setVisibility(View.GONE);
164 | ViewUtils.setLayerTypeNone(mCardHolder);
165 | }
166 | });
167 |
168 | // ALPHA IN OTHER FIELDS
169 | mExtraFields.setVisibility(View.VISIBLE);
170 | ObjectAnimator alphaIn = ObjectAnimator.ofFloat(mExtraFields, "alpha", 1.0f);
171 | alphaIn.setDuration(500);
172 | set.playTogether(translateAnim, alphaOut, alphaIn);
173 | set.start();
174 |
175 | mExpirationEditText.requestFocus();
176 | }
177 |
178 | public interface CardEntryListener {
179 | public void onCardNumberInputComplete();
180 |
181 | public void onEdit();
182 |
183 | public void onCardNumberInputReEntry();
184 |
185 | public void onCVVEntry();
186 |
187 | public void onCVVEntryComplete();
188 |
189 | public void onBackFromCVV();
190 |
191 | }
192 |
193 | private void setCVVMaxLength(int val) {
194 | CVV_MAX_LENGTH = val;
195 | InputFilter[] filters = new InputFilter[1];
196 | filters[0] = new InputFilter.LengthFilter(val);
197 | mCVVEditText.setFilters(filters);
198 | }
199 |
200 | CardEntryListener mCardEntryListener = new CardEntryListener() {
201 | @Override
202 | public void onCardNumberInputComplete() {
203 | validateCard();
204 | }
205 |
206 | @Override
207 | public void onEdit() {
208 | CardType newCardType = ValidateCreditCard.getCardType(mCardHolder.getCardField().getText().toString());
209 | if (newCardType == CardType.AMERICAN_EXPRESS) {
210 | mCardHolder.getCardField().setMaxCardLength(AMEX_CARD_LENGTH);
211 | setCVVMaxLength(4);
212 | } else {
213 | mCardHolder.getCardField().setMaxCardLength(NON_AMEX_CARD_LENGTH);
214 | setCVVMaxLength(3);
215 | }
216 | mCardIcon.setCardType(ValidateCreditCard.getCardType(mCardHolder.getCardField().getText().toString()));
217 | }
218 |
219 | @Override
220 | public void onCardNumberInputReEntry() {
221 | mCardIcon.flipTo(CardFace.FRONT);
222 | AnimatorSet set = new AnimatorSet();
223 |
224 | mCardHolder.getCardField().setVisibility(View.VISIBLE);
225 | ObjectAnimator alphaOut = ObjectAnimator.ofFloat(mExtraFields, "alpha", 0.0f);
226 | alphaOut.setDuration(RE_ENTRY_ALPHA_OUT_DURATION);
227 | alphaOut.addListener(new AnimatorListenerAdapter() {
228 | @Override
229 | public void onAnimationEnd(Animator anim) {
230 | mExtraFields.setVisibility(View.GONE);
231 | mCardHolder.destroyOverlay();
232 | mCardHolder.getCardField().requestFocus();
233 | mCardHolder.getCardField().setSelection(mCardHolder.getCardField().length());
234 | }
235 | });
236 |
237 | ObjectAnimator alphaIn = ObjectAnimator.ofFloat(mCardHolder.getCardField(), "alpha", 0.5f, 1.0f);
238 | alphaIn.setDuration(RE_ENTRY_ALPHA_IN_DURATION);
239 |
240 | ObjectAnimator overShoot = ObjectAnimator.ofFloat(mCardHolder, "translationX", -mCardHolder.getLeftOffset(), 0.0f);
241 | overShoot.setInterpolator(new OvershootInterpolator());
242 | overShoot.setDuration(RE_ENTRY_OVERSHOOT_DURATION);
243 |
244 | set.playTogether(alphaOut, alphaIn, overShoot);
245 | set.start();
246 | }
247 |
248 | @Override
249 | public void onCVVEntry() {
250 | mCardIcon.flipTo(CardFace.BACK);
251 | mCVVEditText.requestFocus();
252 | }
253 |
254 | @Override
255 | public void onCVVEntryComplete() {
256 | mCardIcon.flipTo(CardFace.FRONT);
257 | FieldHolder.this.requestFocus();
258 | // complete
259 | }
260 |
261 | @Override
262 | public void onBackFromCVV() {
263 | mExpirationEditText.requestFocus();
264 | mCardIcon.flipTo(CardFace.FRONT);
265 | }
266 |
267 | };
268 |
269 | public boolean isFieldsValid() {
270 | if (mExpirationEditText.getText().toString().length() != 5) {
271 | return false;
272 | } else if (mCVVEditText.getText().toString().length() != CVV_MAX_LENGTH) {
273 | return false;
274 | }
275 | return true;
276 | }
277 |
278 | }
279 |
--------------------------------------------------------------------------------
/paymentKitDroid/src/main/java/com/paymentkit/ValidateCreditCard.java:
--------------------------------------------------------------------------------
1 | package com.paymentkit;
2 |
3 | /*
4 | * [ValidateCreditCard.java]
5 | *
6 | * PaymnetKit Note: Below is original documentation by Roedy Green. Some parts of this code
7 | * have been modified for PaymentKit.
8 | *
9 | * Summary: Handles calculations to validate credit card numbers and determine which credit card company they belong to.
10 | *
11 | * Copyright: (c) 1999-2013 Roedy Green, Canadian Mind Products, http://mindprod.com
12 | *
13 | * Licence: This software may be copied and used freely for any purpose but military.
14 | * http://mindprod.com/contact/nonmil.html
15 | *
16 | * Requires: JDK 1.6+
17 | *
18 | * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/
19 | *
20 | * Version History:
21 | * 1.0 1999-08-17 posted to comp.lang.java.programmer
22 | * 1.1 1999-08-17 add vendorToString, rename isValid
23 | * implement patterns for 13-16 digit numbers for toPrettyString
24 | * 1.2 1999-08-17 separate enumerations for too few and too many digits.
25 | * 1.3 1999-08-19 ignore dashes in numbers
26 | */
27 |
28 | import static java.lang.System.out;
29 |
30 |
31 |
32 | /**
33 | * Handles calculations to validate credit card numbers and determine which credit card company they belong to.
34 | *
35 | * 1. if a credit card number is valid
36 | * 2. which credit card vendor handles that number.
37 | *
38 | * It validates the prefix and the checkdigit. It does not* contact the credit card company to ensure that number has actually been issued and that
39 | * the account is in good standing.
40 | *
41 | * It will also tell you which of the following credit card companies issued the card: Amex, Diners Club, Carte Blanche, Discover, enRoute, JCB,
42 | * MasterCard or Visa.
43 | *
44 | * @author Roedy Green, Canadian Mind Products
45 | * @version 1.3 1999-08-19 ignore dashes in numbers
46 | * @since 1999-08-17
47 | */
48 | public final class ValidateCreditCard {
49 |
50 | private static final String TAG = ValidateCreditCard.class.getSimpleName();
51 | // ------------------------------ CONSTANTS ------------------------------
52 |
53 | /**
54 | * true if debugging output wanted
55 | */
56 | private static final boolean DEBUGGING = false;
57 |
58 | /**
59 | * Used to speed up findMatchingRange by caching the last hit.
60 | */
61 | private static int cachedLastFind;
62 |
63 | /**
64 | * ranges of credit card number that belong to each company. buildRanges initialises.
65 | */
66 | private static LCR[] ranges;
67 |
68 | // -------------------------- PUBLIC STATIC METHODS --------------------------
69 |
70 | /**
71 | * Determine if the credit card number is valid, i.e. has good prefix and checkdigit. Does _not_ ask the credit card company if this card has been
72 | * issued or is in good standing.
73 | *
74 | * @param creditCardNumber
75 | * number on card.
76 | *
77 | * @return true if card number is good.
78 | */
79 | public static boolean isValid(long creditCardNumber) {
80 | CardType cardType = matchCardType(creditCardNumber);
81 | if (cardType.isError()) {
82 | return false;
83 | } else {
84 | {
85 | // there is a checkdigit to be validated
86 | /*
87 | * Manual method MOD 10 checkdigit 706-511-227 7 0 6 5 1 1 2 2 7 2 * 2 * 2 * 2 --------------------------------- 7 + 0 + 6 +1+0+ 1 + 2 + 2 + 4
88 | * = 23 23 MOD 10 = 3 10 - 3 = 7 -- the check digit Note digits of multiplication results must be added before sum. Computer Method MOD 10
89 | * checkdigit 706-511-227 7 0 6 5 1 1 2 2 7 Z Z Z Z --------------------------------- 7 + 0 + 6 + 1 + 1 + 2 + 2 + 4 + 7 = 30 30 MOD 10 had
90 | * better = 0
91 | */
92 | long number = creditCardNumber;
93 | int checksum = 0;
94 | // work right to left
95 | for (int place = 0; place < 16; place++) {
96 | int digit = (int) (number % 10);
97 | number /= 10;
98 | if ((place & 1) == 0) {
99 | // even position (0-based from right), just add digit
100 | checksum += digit;
101 | } else {// odd position (0-based from right), must double
102 | // and add
103 | checksum += z(digit);
104 | }
105 | if (number == 0) {
106 | break;
107 | }
108 | }// end for
109 | // good checksum should be 0 mod 10
110 | return (checksum % 10) == 0;
111 | }
112 | }
113 | }
114 |
115 | public static CardType getCardType(String number) {
116 | number = number.replaceAll(" ", "");
117 | if(number.toString().length() < 2) return CardType.UNKNOWN_CARD;
118 | String firstChars = number.toString().substring(0, 2);
119 | int range = Integer.parseInt(firstChars);
120 | if (range >= 40 && range <= 49) {
121 | return CardType.VISA;
122 | } else if (range >= 50 && range <= 59) {
123 | return CardType.MASTERCARD;
124 | } else if (range == 34 || range == 37) {
125 | return CardType.AMERICAN_EXPRESS;
126 | } else if (range == 60 || range == 62 || range == 64 || range == 65) {
127 | return CardType.DISCOVER;
128 |
129 | } else {
130 | return CardType.UNKNOWN_CARD;
131 | }
132 | }
133 |
134 | /**
135 | * Finds a matching range in the ranges array for a given creditCardNumber.
136 | *
137 | * @param creditCardNumber
138 | * number on card.
139 | *
140 | * @return index of matching range, or NOT_ENOUGH_DIGITS or UNKNOWN_VENDOR on failure.
141 | */
142 | public static CardType matchCardType(long creditCardNumber) {
143 | if (creditCardNumber < 1000000000000L) {
144 | return CardType.NOT_ENOUGH_DIGITS;
145 | }
146 | if (creditCardNumber > 9999999999999999L) {
147 | return CardType.TOO_MANY_DIGITS;
148 | }
149 | // check the cached index first, where we last found a number.
150 | if (ranges[cachedLastFind].low <= creditCardNumber && creditCardNumber <= ranges[cachedLastFind].high) {
151 | return ranges[cachedLastFind].cardType;
152 | }
153 | for (int i = 0; i < ranges.length; i++) {
154 | if (ranges[i].low <= creditCardNumber && creditCardNumber <= ranges[i].high) {
155 | // we have a match
156 | cachedLastFind = i;
157 | return ranges[i].cardType;
158 | }
159 | }// end for
160 | return CardType.UNKNOWN_CARD;
161 | }// end matchVendor
162 |
163 | /**
164 | * convert a String to a long. The routine is very forgiving. It ignores invalid chars, lead trail, embedded spaces, decimal points etc, AND minus
165 | * signs.
166 | *
167 | * @param numStr
168 | * the String containing the number to be converted to long.
169 | *
170 | * @return long value of the string found, ignoring junk characters. May be negative.
171 | * @throws NumberFormatException
172 | * if the number is too big to fit in a long.
173 | * @see com.mindprod.common11.ST#parseDirtyLong(String)
174 | */
175 | public static long parseDirtyLong(String numStr) {
176 | numStr = numStr.trim();
177 | // strip commas, spaces, + etc, AND -
178 | // StringBuilder is better than FastCat for char by char work.
179 | StringBuilder b = new StringBuilder(numStr.length());
180 | for (int i = 0, n = numStr.length(); i < n; i++) {
181 | char c = numStr.charAt(i);
182 | if ('0' <= c && c <= '9') {
183 | b.append(c);
184 | }
185 | }// end for
186 | numStr = b.toString();
187 | if (numStr.length() == 0) {
188 | return 0;
189 | }
190 | return Long.parseLong(numStr);
191 | }// end parseDirtyLong
192 |
193 | // From http://www.icverify.com/
194 | // Vendor Prefix len checkdigit
195 | // MASTERCARD 51-55 16 mod 10
196 | // VISA 4 13, 16 mod 10
197 | // AMEX 34,37 15 mod 10
198 | // Diners Club/
199 | // Carte Blanche
200 | // 300-305 14
201 | // 36 14
202 | // 38 14 mod 10
203 | // Discover 6011 16 mod 10
204 | // enRoute 2014 15
205 | // 2149 15 any
206 | // JCB 3 16 mod 10
207 | // JCB 2131 15
208 | // 1800 15 mod 10
209 |
210 | /**
211 | * Convert a creditCardNumber as long to a formatted String. Currently it breaks 16-digit numbers into groups of 4.
212 | *
213 | * @param creditCardNumber
214 | * number on card.
215 | *
216 | * @return String representation of the credit card number.
217 | */
218 | public static String toPrettyString(long creditCardNumber) {
219 | String plain = Long.toString(creditCardNumber);
220 | // int i = findMatchingRange(creditCardNumber);
221 | int length = plain.length();
222 | switch (length) {
223 | case 12:
224 | // 12 pattern 3-3-3-3
225 | return plain.substring(0, 3) + ' ' + plain.substring(3, 6) + ' ' + plain.substring(6, 9) + ' ' + plain.substring(9, 12);
226 | case 13:
227 | // 13 pattern 4-3-3-3
228 | return plain.substring(0, 4) + ' ' + plain.substring(4, 7) + ' ' + plain.substring(7, 10) + ' ' + plain.substring(10, 13);
229 | case 14:
230 | // 14 pattern 2-4-4-4
231 | return plain.substring(0, 2) + ' ' + plain.substring(2, 6) + ' ' + plain.substring(6, 10) + ' ' + plain.substring(10, 14);
232 | case 15:
233 | // 15 pattern 3-4-4-4
234 | return plain.substring(0, 3) + ' ' + plain.substring(3, 7) + ' ' + plain.substring(7, 11) + ' ' + plain.substring(11, 15);
235 | case 16:
236 | // 16 pattern 4-4-4-4
237 | return plain.substring(0, 4) + ' ' + plain.substring(4, 8) + ' ' + plain.substring(8, 12) + ' ' + plain.substring(12, 16);
238 | case 17:
239 | // 17 pattern 1-4-4-4-4
240 | return plain.substring(0, 1) + ' ' + plain.substring(1, 5) + ' ' + plain.substring(5, 9) + ' ' + plain.substring(9, 13) + ' '
241 | + plain.substring(13, 17);
242 | default:
243 | // 0..11, 18+ digits long
244 | // plain
245 | return plain;
246 | }// end switch
247 | }// end toPrettyString
248 |
249 | // -------------------------- STATIC METHODS --------------------------
250 |
251 | static {
252 | // now that all enum constants defined
253 | buildRanges();
254 | }
255 |
256 | /**
257 | * build table of which ranges of credit card number belong to which vendor
258 | */
259 | private static void buildRanges() {
260 | // careful, no lead zeros allowed
261 | // low high len vendor
262 | ranges = new LCR[] { new LCR(4000000000000L, 4999999999999L/* 13 */, CardType.VISA),
263 | new LCR(340000000000000L, 349999999999999L/* 15 */, CardType.AMERICAN_EXPRESS),
264 | new LCR(370000000000000L, 379999999999999L/* 15 */, CardType.AMERICAN_EXPRESS),
265 | new LCR(4000000000000000L, 4999999999999999L/* 16 */, CardType.VISA),
266 | new LCR(5100000000000000L, 5599999999999999L/* 16 */, CardType.MASTERCARD),
267 | new LCR(6011000000000000L, 6011999999999999L/* 16 */, CardType.DISCOVER) };
268 | }
269 |
270 | /**
271 | * used in computing checksums, doubles and adds resulting digits.
272 | *
273 | * @param digit
274 | * the digit to be doubled, and digit summed.
275 | *
276 | * @return // 0->0 1->2 2->4 3->6 4->8 5->1 6->3 7->5 8->7 9->9
277 | */
278 | private static int z(int digit) {
279 | if (digit == 0) {
280 | return 0;
281 | } else {
282 | return (digit * 2 - 1) % 9 + 1;
283 | }
284 | }
285 |
286 | // --------------------------- main() method ---------------------------
287 |
288 | /**
289 | * Test driver
290 | *
291 | * @param args
292 | * not used
293 | */
294 | public static void main(String[] args) {
295 | if (DEBUGGING) {
296 | out.println(matchCardType(0));// not enough digits
297 | out.println(matchCardType(6011222233334444L));// Discover
298 | out.println(matchCardType(6010222233334444L));// unknown vendor
299 | out.println(matchCardType(4000000000000L));// Visa
300 | out.println(matchCardType(4999999999999L));// Visa
301 | out.println(isValid(0));// false
302 | out.println(isValid(6010222233334444L));// false
303 | out.println(isValid(4000000000000L));// false
304 | out.println(isValid(4000000000006L));// true
305 | out.println(isValid(4000000000009L));// false
306 | out.println(isValid(4999999999999L));// false
307 | out.println(isValid(378888888888858L));// true, Amex
308 | out.println(isValid(4888888888888838L));// true, Visa;
309 | out.println(isValid(5588888888888838L));// true, MC
310 | out.println(isValid(6011222233334444L));// true, Discover
311 | out.println(parseDirtyLong("123,444 999=z/99"));// 12344499999
312 | out.println(toPrettyString(0));// 0
313 | out.println(toPrettyString(6011222233334444L));// 6011 2222 3333 4444
314 | out.println(toPrettyString(6010222233334444L));// 6010 2222 3333 4444
315 | out.println(toPrettyString(4000000000000L));// 4000 000 000 000
316 | out.println(toPrettyString(4000000000006L));// 4000 000 000 006
317 | out.println(toPrettyString(4000000000009L));// 4000 000 000 009
318 | out.println(toPrettyString(3123456789012341L));// 3123 4567 8901 2341
319 | out.println(toPrettyString(999999999990L));// 999 999 999 990
320 | out.println(toPrettyString(4000000000006L));// 4000 000 000 006
321 | out.println(toPrettyString(30000000000004L));// 30 0000 0000 0004
322 | out.println(toPrettyString(180000000000002L));// 180 0000 0000 0002
323 | out.println(toPrettyString(3000000000000004L));// 3000 0000 0000 0004
324 | out.println(toPrettyString(3000000000000005L));// 3000 0000 0000 0005
325 | out.println(toPrettyString(13000000000000005L));// 1 3000 0000 0000 0005
326 | out.println(CardType.VISA.getName());// Visa
327 | out.println(CardType.UNKNOWN_CARD.getName());// Error: unknown credit card company
328 | }// end if debugging
329 | }// end main
330 | }
331 |
332 | /**
333 | * Describes a single Legal Card Range
334 | */
335 | final class LCR {
336 | // ------------------------------ FIELDS ------------------------------
337 |
338 | /**
339 | * enumeration credit card service
340 | */
341 | public final CardType cardType;
342 |
343 | /**
344 | * low and high bounds on range covered by this vendor
345 | */
346 | public final long high;
347 |
348 | /**
349 | * low bounds on range covered by this vendor
350 | */
351 | public final long low;
352 |
353 | // -------------------------- PUBLIC INSTANCE METHODS --------------------------
354 |
355 | /**
356 | * public constructor
357 | *
358 | * @param low
359 | * lowest credit card number in range.
360 | * @param high
361 | * highest credit card number in range
362 | * @param cardType
363 | * enum constant for vendor
364 | */
365 | public LCR(long low, long high, CardType cardType) {
366 | this.low = low;
367 | this.high = high;
368 | this.cardType = cardType;
369 | }// end public constructor
370 | }
371 |
372 |
373 |
--------------------------------------------------------------------------------