Skip to content

Instantly share code, notes, and snippets.

@cjae
Last active August 3, 2020 22:55
Show Gist options
  • Save cjae/260c456a3ef6104c34f219ec37f82a5a to your computer and use it in GitHub Desktop.
Save cjae/260c456a3ef6104c34f219ec37f82a5a to your computer and use it in GitHub Desktop.
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="none"
android:background="@color/app_white"
tools:context=".features.cardpayment.CardPaymentActivity">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<FrameLayout
android:id="@+id/back_button"
android:layout_width="?attr/actionBarSize"
android:layout_height="?attr/actionBarSize"
android:background="?selectableItemBackgroundBorderless"
android:clickable="true"
android:focusable="true"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:srcCompat="@drawable/round_chevron_left_24"
app:tint="@color/primary_text_color" />
</FrameLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/flightModeView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/payment"
android:textSize="@dimen/_16ssp"
fontPath="fonts/OpenSans-SemiBold.ttf"
android:textColor="@color/primary_text_color"
app:layout_constraintTop_toTopOf="@id/back_button"
app:layout_constraintBottom_toBottomOf="@id/back_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/cardTypesView"
android:src="@drawable/card_types"
android:layout_width="match_parent"
android:layout_height="@dimen/_100sdp"
android:layout_marginTop="@dimen/_15sdp"
app:layout_constraintTop_toBottomOf="@id/back_button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
<LinearLayout
android:id="@+id/amountView"
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:padding="@dimen/_10sdp"
android:background="@drawable/border_grey_frame"
android:layout_marginTop="@dimen/_15sdp"
android:layout_marginStart="@dimen/_20sdp"
android:layout_marginEnd="@dimen/_20sdp"
app:layout_constraintTop_toBottomOf="@id/cardTypesView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/amountOutstandingLabelView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/total_payable"
android:gravity="center"
android:textSize="@dimen/_12ssp"
android:textColor="@color/secondary_text_color"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/amountPayableTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/dash_text"
android:gravity="center"
android:textSize="@dimen/_18ssp"
android:layout_margin="@dimen/_3sdp"
fontPath="fonts/OpenSans-SemiBold.ttf"
android:textColor="@color/primary_text_color"/>
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/additional_transaction_fee"
android:gravity="center"
android:textSize="@dimen/_10ssp"
android:textColor="@color/app_red_seat"/>
</LinearLayout>
<studio.carbonylgroup.textfieldboxes.TextFieldBoxes
android:id="@+id/cardNumberView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:labelText="@string/card_number"
app:hasClearButton="true"
android:layout_marginTop="@dimen/_15sdp"
app:layout_constraintTop_toBottomOf="@id/amountView"
app:layout_constraintStart_toStartOf="@id/amountView"
app:layout_constraintEnd_toEndOf="@id/amountView">
<studio.carbonylgroup.textfieldboxes.ExtendedEditText
android:id="@+id/cardNumberTextBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/primary_text_color"
android:textColorHint="@color/hint_text_color"
android:imeOptions="actionNext"
android:textSize="@dimen/_14ssp"
android:inputType="number"
android:maxLines="1"/>
</studio.carbonylgroup.textfieldboxes.TextFieldBoxes>
<studio.carbonylgroup.textfieldboxes.TextFieldBoxes
android:id="@+id/cardMonthView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:labelText="@string/mm"
app:hasClearButton="true"
android:layout_marginTop="@dimen/_15sdp"
app:layout_constraintTop_toBottomOf="@id/cardNumberView"
app:layout_constraintStart_toStartOf="@id/cardNumberView"
app:layout_constraintEnd_toStartOf="@id/cardYearView">
<studio.carbonylgroup.textfieldboxes.ExtendedEditText
android:id="@+id/cardMonthViewTextBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/primary_text_color"
android:textColorHint="@color/hint_text_color"
android:imeOptions="actionNext"
android:maxLength="2"
android:textSize="@dimen/_14ssp"
android:inputType="number"
android:maxLines="1"/>
</studio.carbonylgroup.textfieldboxes.TextFieldBoxes>
<studio.carbonylgroup.textfieldboxes.TextFieldBoxes
android:id="@+id/cardYearView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:labelText="@string/yy"
app:hasClearButton="true"
android:layout_marginStart="@dimen/_5sdp"
android:layout_marginEnd="@dimen/_5sdp"
app:layout_constraintTop_toTopOf="@id/cardMonthView"
app:layout_constraintBottom_toBottomOf="@id/cardMonthView"
app:layout_constraintStart_toEndOf="@id/cardMonthView"
app:layout_constraintEnd_toStartOf="@id/cardCVVView">
<studio.carbonylgroup.textfieldboxes.ExtendedEditText
android:id="@+id/cardYearViewTextBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/primary_text_color"
android:textColorHint="@color/hint_text_color"
android:imeOptions="actionNext"
android:maxLength="2"
android:textSize="@dimen/_14ssp"
android:inputType="number"
android:maxLines="1"/>
</studio.carbonylgroup.textfieldboxes.TextFieldBoxes>
<studio.carbonylgroup.textfieldboxes.TextFieldBoxes
android:id="@+id/cardCVVView"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:labelText="@string/cvv"
app:hasClearButton="true"
app:layout_constraintTop_toTopOf="@id/cardMonthView"
app:layout_constraintBottom_toBottomOf="@id/cardMonthView"
app:layout_constraintStart_toEndOf="@id/cardYearView"
app:layout_constraintEnd_toEndOf="@id/cardNumberView">
<studio.carbonylgroup.textfieldboxes.ExtendedEditText
android:id="@+id/cardCVVViewTextBox"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@color/primary_text_color"
android:textColorHint="@color/hint_text_color"
android:imeOptions="actionDone"
android:maxLength="3"
android:textSize="@dimen/_14ssp"
android:inputType="number"
android:maxLines="1"/>
</studio.carbonylgroup.textfieldboxes.TextFieldBoxes>
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/payNowButton"
android:layout_width="0dp"
android:layout_height="@dimen/button_height"
android:text="@string/pay_now"
android:layout_marginTop="@dimen/_14sdp"
android:layout_marginBottom="@dimen/_20sdp"
android:textSize="@dimen/_14ssp"
android:textAllCaps="false"
android:textColor="@color/app_white"
app:layout_constraintTop_toBottomOf="@id/cardMonthView"
app:layout_constraintStart_toStartOf="@id/cardNumberView"
app:layout_constraintEnd_toEndOf="@id/cardNumberView"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/_10sdp"
android:layout_marginBottom="@dimen/_20sdp"
app:layout_constraintTop_toBottomOf="@id/payNowButton"
app:layout_constraintStart_toStartOf="@id/payNowButton"
app:layout_constraintEnd_toEndOf="@id/payNowButton"
app:layout_constraintBottom_toBottomOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/ic_lock"
android:tint="@color/icon_color"/>
<androidx.appcompat.widget.AppCompatTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/secured_by_paystack"
android:textColor="@color/secondary_text_color"/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
public class CardPaymentActivity extends BaseActivity {
@BindView(R.id.cardNumberView)
TextFieldBoxes cardNumberView;
@BindView(R.id.cardMonthView)
TextFieldBoxes cardMonthView;
@BindView(R.id.cardYearView)
TextFieldBoxes cardYearView;
@BindView(R.id.cardCVVView)
TextFieldBoxes cardCVVView;
@BindView(R.id.cardNumberTextBox)
ExtendedEditText cardNumberTextBox;
@BindView(R.id.cardMonthViewTextBox)
ExtendedEditText cardMonthViewTextBox;
@BindView(R.id.cardYearViewTextBox)
ExtendedEditText cardYearViewTextBox;
@BindView(R.id.cardCVVViewTextBox)
ExtendedEditText cardCVVViewTextBox;
@BindView(R.id.amountPayableTextView)
AppCompatTextView amountPayableTextView;
// Class Properties
private String accessCode;
private double amount;
private String email;
private CountDownTimer activityCountDownTimer;
private CustomProgressDialog customProgressDialog;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_card_payment);
fetchDataFromBundle();
initHelpers();
initWatchers();
initViews();
}
private void fetchDataFromBundle() {
accessCode = getIntent().getStringExtra(PAYMENT_ACCESS_CODE_OBJECT);
amount = getIntent().getDoubleExtra(PAYMENT_AMOUNT_OBJECT, 0);
email = getIntent().getStringExtra(PAYMENT_EMAIL_OBJECT);
}
private void initHelpers() {
customProgressDialog = new CustomProgressDialog(this);
}
private void initWatchers() {
cardNumberTextBox.addTextChangedListener(cardWatcher);
cardMonthViewTextBox.addTextChangedListener(new ExpiryWatcher(cardMonthViewTextBox));
cardYearViewTextBox.addTextChangedListener(new ExpiryWatcher(cardYearViewTextBox));
}
private void initViews() {
String parsedAmount = parseAmount(String.valueOf(amount));
if (!TextUtils.isEmpty(parsedAmount)) {
amountPayableTextView.setText(String.format("₦%1$s", parsedAmount));
}
}
public void showProgress(boolean show) {
if (show) {
customProgressDialog.showDialog();
} else {
customProgressDialog.hideDialog();
}
}
public void showErrorDialog(String message) {
AppSuccessErrorDialog appSuccessErrorDialog = new AppSuccessErrorDialog(this,
DialogType.TYPE_ERROR, message);
appSuccessErrorDialog.showDialog();
}
private void doShowSessionExpiredDialog() {
SessionExpiredDialog sessionExpiredDialog = new SessionExpiredDialog(this);
sessionExpiredDialog.setCallBack(this::doGotoHomeScreen);
sessionExpiredDialog.showDialog();
}
private void doGotoHomeScreen() {
Intent intent = new Intent(this, Main2Activity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
}
// Click Methods--------------------------------------------------------------------------------
@OnClick(R.id.back_button)
void onBackButtonClicked() {
onBackPressed();
}
@OnClick(R.id.payNowButton)
void onPayNowButtonClicked() {
String cardNum = cardNumberTextBox.getText().toString().trim();
String cvv = cardCVVViewTextBox.getText().toString().trim();
String sMonth = cardMonthViewTextBox.getText().toString().trim();
String sYear = cardYearViewTextBox.getText().toString().trim();
if (TextUtils.isEmpty(cardNum)) {
cardNumberView.setError("Empty card number", true);
return;
}
if (CommonUtils.inputShort(cardNum, 16)) {
cardNumberView.setError("Invalid card number", true);
return;
}
if (TextUtils.isEmpty(sMonth)) {
cardMonthView.setError("Required", true);
return;
}
if (TextUtils.isEmpty(sYear)) {
cardYearView.setError("Required", true);
return;
}
if (TextUtils.isEmpty(cvv)) {
cardCVVView.setError("Required", true);
return;
}
if (CommonUtils.inputShort(cvv, 3)) {
cardCVVView.setError("Invalid", true);
return;
}
//do Check Card
}
// Overridden Methods---------------------------------------------------------------------------
@Override
protected void onDestroy() {
cardNumberTextBox.removeTextChangedListener(cardWatcher);
super.onDestroy();
}
// Text Watchers--------------------------------------------------------------------------------
private TextWatcher cardWatcher = new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {/*EMPTY METHOD*/}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {/*EMPTY METHOD*/}
@Override
public void afterTextChanged(Editable s) {
String number = s.toString();
if (number.length() >= 4) {
String formatted = CommonUtils.formatForViewing(number, 4);
if (!number.equalsIgnoreCase(formatted)) {
cardNumberTextBox.removeTextChangedListener(cardWatcher);
cardNumberTextBox.setText(formatted);
cardNumberTextBox.setSelection(formatted.length());
cardNumberTextBox.addTextChangedListener(cardWatcher);
}
}
}
};
private class ExpiryWatcher implements TextWatcher {
private ExtendedEditText editText;
ExpiryWatcher(ExtendedEditText editText) {
this.editText = editText;
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {/*EMPTY METHOD*/}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {/*EMPTY METHOD*/}
@Override
public void afterTextChanged(Editable s) {
try {
int number = Integer.parseInt(s.toString());
int length = s.length();
switch (editText.getId()) {
case R.id.cardMonthViewTextBox:
checkCardMonth(length, number);
break;
case R.id.cardYearViewTextBox:
checkCardYear(length, number);
break;
}
} catch (NumberFormatException nfe) {
nfe.printStackTrace();
}
}
private void checkCardMonth(int length, int number) {
if (length == 1) {
if (number > 1) {
//add a 0 in front
setText("0"+number);
}
} else {
if (number > 12) {
setText("12");
}
//request focus on the next field
cardYearView.requestFocus();
}
}
private void checkCardYear(int length, int number) {
String stringYear = (Calendar.getInstance().get(Calendar.YEAR) + "").substring(2);
int currentYear = Integer.parseInt(stringYear);
if (length == 1) {
int firstDigit = Integer.parseInt(String.valueOf(currentYear).substring(0, length));
if(number < firstDigit) {
setText(firstDigit+"");
}
} else {
if (number < currentYear){
setText(currentYear+"");
}
cardCVVView.requestFocus();
}
}
private void setText(String text) {
editText.setText(text);
editText.setSelection(editText.getText().toString().trim().length());
}
}
}
public class CommonUtils {
private static String cleanNumber(String number) {
return number.replaceAll("\\s", "");
}
public static String formatForViewing(String enteredNumber, int maxLength) {
String cleaned = cleanNumber(enteredNumber);
int len = cleaned.length();
if (len <= maxLength)
return cleaned;
ArrayList<String> gaps = new ArrayList<>();
int[] segmentLengths = {0, 0, 0, 0};
// { 4-4-4-4-4}
gaps.add(" ");
segmentLengths[0] = 4;
gaps.add(" ");
segmentLengths[1] = 4;
gaps.add(" ");
segmentLengths[2] = 4;
gaps.add(" ");
segmentLengths[3] = 4;
int end = maxLength;
int start;
String segment1 = cleaned.substring(0, end);
start = end;
end = Math.min(segmentLengths[0] + end, len);
String segment2 = cleaned.substring(start, end);
start = end;
end = Math.min(segmentLengths[1] + end, len);
String segment3 = cleaned.substring(start, end);
start = end;
end = Math.min(segmentLengths[2] + end, len);
String segment4 = cleaned.substring(start, end);
start = end;
end = Math.min(segmentLengths[3] + end, len);
String segment5 = cleaned.substring(start, end);
String ret = String.format("%s%s%s%s%s%s%s%s%s",
segment1, gaps.get(0),
segment2, gaps.get(1),
segment3, gaps.get(2),
segment4, gaps.get(2),
segment5);
return ret.trim();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment