From f95fac9f6e0ec5138dd6cf0d181947aab30f2382 Mon Sep 17 00:00:00 2001 From: ahmeddatexpay Date: Wed, 24 Sep 2025 20:11:47 +0300 Subject: [PATCH] add pin key keyboard --- app/app.keystore | Bin 0 -> 2195 bytes app/build.gradle | 30 +- .../cardreadtest/payment/PaymentActivity.java | 49 +++ .../payment/PaymentViewModel.java | 15 +- .../pinkeyboard/KeyBoardNumInterface.java | 10 + .../payment/pinkeyboard/KeyboardTool.java | 66 ++++ .../payment/pinkeyboard/KeyboardUtil.java | 225 ++++++++++++ .../payment/pinkeyboard/MyKeyboardView.java | 331 ++++++++++++++++++ .../payment/pinkeyboard/PinPadDialog.java | 56 +++ .../payment/pinkeyboard/PinPadView.java | 282 +++++++++++++++ .../res/drawable/keyboard_keybg_white.xml | 15 + app/src/main/res/drawable/keyboard_white.xml | 7 + app/src/main/res/layout/activity_payment.xml | 61 +++- .../main/res/layout/include_keyboardview.xml | 51 +++ app/src/main/res/layout/include_pinpad.xml | 56 +++ .../main/res/layout/view_paypass_dialog.xml | 14 + .../res/layout/view_paypass_gridview_item.xml | 23 ++ .../main/res/layout/view_paypass_layout.xml | 49 +++ .../main/res/mipmap-xhdpi/ic_pay_close.png | Bin 0 -> 600 bytes app/src/main/res/mipmap-xhdpi/ic_pay_del0.png | Bin 0 -> 1307 bytes app/src/main/res/mipmap-xhdpi/ic_pay_del1.png | Bin 0 -> 15769 bytes .../main/res/mipmap-xxhdpi/icon_delete.png | Bin 0 -> 3212 bytes .../main/res/mipmap-xxhdpi/icon_delete_1.png | Bin 0 -> 1522 bytes app/src/main/res/mipmap-xxhdpi/left.png | Bin 0 -> 1307 bytes app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/keyboard_number.xml | 1 + app/src/main/res/xml/keyboard_only_number.xml | 1 + app/src/main/res/xml/keyboard_symbol.xml | 150 ++++++++ 28 files changed, 1481 insertions(+), 13 deletions(-) create mode 100644 app/app.keystore create mode 100644 app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyBoardNumInterface.java create mode 100644 app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyboardTool.java create mode 100644 app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyboardUtil.java create mode 100644 app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/MyKeyboardView.java create mode 100644 app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/PinPadDialog.java create mode 100644 app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/PinPadView.java create mode 100644 app/src/main/res/drawable/keyboard_keybg_white.xml create mode 100644 app/src/main/res/drawable/keyboard_white.xml create mode 100644 app/src/main/res/layout/include_keyboardview.xml create mode 100644 app/src/main/res/layout/include_pinpad.xml create mode 100644 app/src/main/res/layout/view_paypass_dialog.xml create mode 100644 app/src/main/res/layout/view_paypass_gridview_item.xml create mode 100644 app/src/main/res/layout/view_paypass_layout.xml create mode 100644 app/src/main/res/mipmap-xhdpi/ic_pay_close.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_pay_del0.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_pay_del1.png create mode 100644 app/src/main/res/mipmap-xxhdpi/icon_delete.png create mode 100644 app/src/main/res/mipmap-xxhdpi/icon_delete_1.png create mode 100644 app/src/main/res/mipmap-xxhdpi/left.png create mode 100644 app/src/main/res/xml/keyboard_number.xml create mode 100644 app/src/main/res/xml/keyboard_only_number.xml create mode 100644 app/src/main/res/xml/keyboard_symbol.xml diff --git a/app/app.keystore b/app/app.keystore new file mode 100644 index 0000000000000000000000000000000000000000..15a8b63e49fcadd4eb2dc2e180a908e2252f8ef4 GIT binary patch literal 2195 zcmcIk`8U*y8=lWB7-Od~k-em`eVAlV))JC#vJ4|4hCvdtGf0-i2#tvBp(t+IC&|(s z)o2Jw*2&jWF_W^pzW03ZIsF6Q_nha4=Xrm4&-=XZd3L_;e1$+D&^-YE0OcJL;OXw~ z7Df#4-qWs@NOEUEAP6W(hVOs^T)fI$035(Vg#iEofs^420U2yX;HE{nI$h%1hDp%* z(eT@h+M=0go{2JdmC~UP(0oMm8Zc8+I}cpd@+yPsac` zviv0-Xn{~8#e#kQ!L3Pan^MA*iJ;fWu`R3O-aM~#V=J|X&dH~aQVe#B+FEaU-3fDq z3uXE)A|k?1q|c>KVk6q`g?c++hsNZ^STI%C$+Yj4h?cg8)$eRB?r4)M@JqFC7V2}$ ziW^i*^ml^Q!?JC8raa;{228fHhponagi<~)JVxfwtIlxl6L3)g!P#WvSC_Z5AR8IfyAamwDu&f$<}qh#J4p0V8ph16?&oFNhY# zcYe}J0VT3hxPqn6%0Io58?%uRdzqfR&D(psK1S6oEb^IsvA5pa(>k-Bf8zF=dRzTf z_8=i`&VAv6+(wdx_p6f|e#zH}9K~*^j6~bKxXu%CrrfK3V>T7+@RsHarN?9iS|3=^QR%eHhO56ARm@Jfg` z!hTe$e~yUPdj8;aIa;@Qh4?G5nMtg5FmDq%s1Z9PHeAF>taKvjec zStPyX7xbq%)>wW&yYNOc3Cr#qCua1SO~Gwd&5S0B z8f(;T*XNpwD>IeO*7I2Vl16&Z*uwQ<16M1x;$e1W>?9>9Ykfv^(WdW71=g1^6TKDm z(wWQ97iKY0C1as4kkI1jXT<#$z^8d}FH z375uRH-5|56l}*?CeB|>(Jv{}e1URRMtW_~w$=EGF{#RKMf>mVIJg>yN~eeP=iRAY zSwB2$*`v8+?cp#^TCGayOr^D_&-?Gwc>3|c%?aAeerQ7pw;ZrR(X75Hl4B5+N_4BH zT^hOiBVQr9aoXzSK^0l7nOL!kYy2-n6ryRR1@d7o3Vw1!@+xfu|SjkQ(hOFim`{S%J}M>Q=_ z7XpEqgJhU7NQNFMhCu-+6s{P*p9Au9A+a}3-T1{104M|mJgdPEqPSr8a4u0exK|n| z5cyxqBMSBK{g-h6PIyJ3o<9F}P`?wHDAdm%lojXK1i@n(8sIUUuEtSkjG!i{tp(yV zwEidjA7+vP`QI1Y%Y7Ib5QKnafEP*z07&jQetc(+G|61(DhNw+Zelc*2Y%yiv%~F_ zUq$zEO>xRrBhNL}I8+_K##P&`qu|>8G~T%Xkkj(f8e-)TGEeyNk%_>yj(53qslS#b zGF3gG0p^lt3Ouruqzd$e^s7H+1K2D2M;LsP0(c|A-5+!XBO#=f{CjR|UzeL_gGm|* z>FJXBk1UCH?*zmLg%8;FE=LVU(Vj+_=S#K%l3r(Rg00NZCfij@f-f^V_;AptmAa1kOv0t}5Z2NiF zFCL!w*`g+;-%Z^6jy)3r$hkthxLHi+13t*+`3Y{-@K}vY$wss$FFpMR>yi_)4D-+T zi<;PX1Hsu%8dbDcxVJN+Nv1brCZU`&=%t=>K4f9^LuNBdNULiMx8w0j&pZBC+s&}q zOa*s`evt(X_Fl2a1vF64UfEGb@(cLZ%;P^XFLxk#JngMrZkg6lpJb15{CDY_E7Yo> zk0|z?QFUf+v@z~Wk(uD)5T{#h%Z8@jrXy}D(eNpHDN?lBNzM_ea+RQIs=DD?!}$2K zCYEDqlUp;?B|agHa!hovpp4Zm_wU=i&MdcyL`F6DOv^KbTLdZD=sL}Tft&vdMwKj5y3Z&SWYtRl4YOp*)Bpeg literal 0 HcmV?d00001 diff --git a/app/build.gradle b/app/build.gradle index da323f3..b567b59 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -7,18 +7,32 @@ android { compileSdk 35 signingConfigs { +// release { +// storeFile file('new-keystore.jks') +// storePassword 'MPOS73356xDp' +// keyPassword 'MPOS73356xDp' +// keyAlias 'mulberrypos' +// } +// debug { +// storeFile file('new-keystore.jks') +// storePassword 'MPOS73356xDp' +// keyPassword 'MPOS73356xDp' +// keyAlias 'mulberrypos' +// } + release { - storeFile file('new-keystore.jks') - storePassword 'MPOS73356xDp' - keyPassword 'MPOS73356xDp' - keyAlias 'mulberrypos' + storeFile file('app.keystore') + storePassword 'dspread' + keyPassword 'dspread' + keyAlias 'gundam_wing' } debug { - storeFile file('new-keystore.jks') - storePassword 'MPOS73356xDp' - keyPassword 'MPOS73356xDp' - keyAlias 'mulberrypos' + storeFile file('app.keystore') + storePassword 'dspread' + keyPassword 'dspread' + keyAlias 'gundam_wing' } + } diff --git a/app/src/main/java/com/test/cardreadtest/payment/PaymentActivity.java b/app/src/main/java/com/test/cardreadtest/payment/PaymentActivity.java index 97940d2..0fa4649 100644 --- a/app/src/main/java/com/test/cardreadtest/payment/PaymentActivity.java +++ b/app/src/main/java/com/test/cardreadtest/payment/PaymentActivity.java @@ -18,11 +18,18 @@ import com.test.cardreadtest.posAPI.PaymentResult; import com.test.cardreadtest.posAPI.PaymentServiceCallback; import com.test.cardreadtest.utils.TRACE; +import com.test.cardreadtest.payment.pinkeyboard.KeyboardUtil; +import com.test.cardreadtest.payment.pinkeyboard.MyKeyboardView; +import com.test.cardreadtest.payment.pinkeyboard.PinPadDialog; +import com.test.cardreadtest.payment.pinkeyboard.PinPadView; + import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.List; + + public class PaymentActivity extends AppCompatActivity { private static final String TAG = "PaymentActivity"; private String amount; @@ -30,6 +37,13 @@ public class PaymentActivity extends AppCompatActivity { private PaymentViewModel viewModel; private ActivityPaymentBinding binding; private PaymentServiceCallback paymentServiceCallback; + private KeyboardUtil keyboardUtil; + private int timeOfPinInput; + public PinPadDialog pinPadDialog; + private int changePinTimes; + private boolean isPinBack = false; + private boolean isChangePin = false; + @Override protected void onCreate(Bundle savedInstanceState) { @@ -151,6 +165,41 @@ public class PaymentActivity extends AppCompatActivity { @Override public void onQposRequestPinResult(List dataList, int offlineTime) { TRACE.d("onQposRequestPinResult = " + dataList + "\nofflineTime: " + offlineTime); + + if (POSManager.getInstance().isDeviceReady()) { + viewModel.setWaitingStatus(false); + viewModel.showPinpad.set(true); + boolean onlinePin = POSManager.getInstance().isOnlinePin(); + if (keyboardUtil != null) { + keyboardUtil.hide(); + } + if (isChangePin) { + if (timeOfPinInput == 1) { + } else if (timeOfPinInput == 2) { + timeOfPinInput = 0; + } + } else { + if (onlinePin) { + } else { + int cvmPinTryLimit = POSManager.getInstance().getCvmPinTryLimit(); + TRACE.d("PinTryLimit:" + cvmPinTryLimit); + if (cvmPinTryLimit == 1) { + } else { + } + } + } + } + binding.pinpadEditText.setText(""); + MyKeyboardView.setKeyBoardListener(value -> { + if (POSManager.getInstance().isDeviceReady()) { + POSManager.getInstance().pinMapSync(value, 20); + } + }); + if (POSManager.getInstance().isDeviceReady()) { + keyboardUtil = new KeyboardUtil(PaymentActivity.this, binding.scvText, dataList); + keyboardUtil.initKeyboard(MyKeyboardView.KEYBOARDTYPE_Only_Num_Pwd, binding.pinpadEditText);//Random keyboard + } + } @Override diff --git a/app/src/main/java/com/test/cardreadtest/payment/PaymentViewModel.java b/app/src/main/java/com/test/cardreadtest/payment/PaymentViewModel.java index b672cf8..2986263 100644 --- a/app/src/main/java/com/test/cardreadtest/payment/PaymentViewModel.java +++ b/app/src/main/java/com/test/cardreadtest/payment/PaymentViewModel.java @@ -1,5 +1,7 @@ package com.test.cardreadtest.payment; +import androidx.databinding.ObservableBoolean; +import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; import androidx.lifecycle.ViewModel; @@ -11,6 +13,7 @@ public class PaymentViewModel extends ViewModel { private MutableLiveData cardInfo = new MutableLiveData<>("Card: --"); private MutableLiveData showProgress = new MutableLiveData<>(false); private MutableLiveData showResults = new MutableLiveData<>(false); + public ObservableBoolean showPinpad = new ObservableBoolean(false); public PaymentViewModel() { super(); @@ -36,10 +39,6 @@ public class PaymentViewModel extends ViewModel { return showProgress; } - public MutableLiveData getShowResults() { - return showResults; - } - public void displayAmount(String amountValue) { amount.postValue("Amount: $" + amountValue); } @@ -63,6 +62,14 @@ public class PaymentViewModel extends ViewModel { } } + public LiveData getShowResults() { + return showResults; + } + + public void setShowResults(Boolean value) { + showResults.setValue(value); + } + public void showTransactionResults(boolean show) { showResults.postValue(show); } diff --git a/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyBoardNumInterface.java b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyBoardNumInterface.java new file mode 100644 index 0000000..1bd4cbd --- /dev/null +++ b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyBoardNumInterface.java @@ -0,0 +1,10 @@ +package com.test.cardreadtest.payment.pinkeyboard; + +/** + * Time:2020/4/26 + * Author:Qianmeng Chen + * Description: + */ +public interface KeyBoardNumInterface { + void getNumberValue(String Value); +} diff --git a/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyboardTool.java b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyboardTool.java new file mode 100644 index 0000000..6ff92ed --- /dev/null +++ b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyboardTool.java @@ -0,0 +1,66 @@ +package com.test.cardreadtest.payment.pinkeyboard; + +import android.app.Activity; +import android.content.Context; +import android.view.MotionEvent; +import android.view.View; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; + +/** + * **************************************************************** + * File Name: KeyboardTool + * File Description: Keyboard Tool + * **************************************************************** + */ +public class KeyboardTool { + /** + * hide keyboard + * + * @param v The focus view + * @param views Input box + * @return true means the focus is on edit + */ + public static boolean isFocusEditText(View v, View... views) { + if (v instanceof EditText && views != null && views.length > 0) { + for (View view : views) { + if (v == view) { + return true; + } + } + } + return false; + } + + /* + * whether touch specified view + **/ + public static boolean isTouchView(View[] views, MotionEvent ev) { + if (views == null || views.length == 0) { + return false; + } + int[] location = new int[2]; + for (View view : views) { + view.getLocationOnScreen(location); + int x = location[0]; + int y = location[1]; + if (ev.getX() > x && ev.getX() < (x + view.getWidth()) && ev.getY() > y && ev.getY() < (y + view.getHeight())) { + return true; + } + } + return false; + } + + /** + * hide soft keyboard + */ + public static void hideInputForce(Activity activity, View currentFocusView) { + if (activity == null || currentFocusView == null) { + return; + } + InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.hideSoftInputFromWindow(currentFocusView.getWindowToken(), 0); + } + } +} diff --git a/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyboardUtil.java b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyboardUtil.java new file mode 100644 index 0000000..1f567dd --- /dev/null +++ b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/KeyboardUtil.java @@ -0,0 +1,225 @@ +package com.test.cardreadtest.payment.pinkeyboard; + +import android.app.Activity; +import android.graphics.Rect; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowManager; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.PopupWindow; + +import com.test.cardreadtest.R; + +import java.lang.reflect.Method; +import java.util.List; + +/** + * **************************************************************** + * File Name: KeyboardUtil + * File Description: Keyboard Util + * **************************************************************** + */ +public class KeyboardUtil { + private Activity mActivity; + private View mParent; + + private PopupWindow mWindow; + private MyKeyboardView mKeyboardView; + private boolean needInit; + private boolean mScrollTo = false;//whether the interface moves up + // private int mEditTextHeight;//edit text height 44dp + private int mKeyboardHeight;//keyboard height 260dp + private int mHeightPixels;//screen height + private int mKeyBoardMarginEditTextTopHeight;//the minimum distance between the keyboard and the top of the edit text + private List dataList; + public static EditText pinpadEditText; + public KeyboardUtil(Activity context, View parent, List dataList) { + this.dataList = dataList; + this.mActivity = context; + this.mParent = parent; + LinearLayout mIncludeKeyboardview = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.include_keyboardview, null); +// RelativeLayout mKeyboardTopView = (RelativeLayout) mIncludeKeyboardview.findViewById(R.id.keyboard_top_rl); + mKeyboardView = (MyKeyboardView) mIncludeKeyboardview.findViewById(R.id.keyboard_view); + mWindow = new PopupWindow(mIncludeKeyboardview, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, false); +// mWindow.setAnimationStyle(R.style.AnimBottom); //Animation style + mWindow.setOnDismissListener(mOnDismissListener); + mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);//prevent being blocked by the bottom toolbar + int mEditTextHeight = dp2px(44);//44dp edit text height + mKeyboardHeight = dp2px(260);//260dp + mKeyBoardMarginEditTextTopHeight = mEditTextHeight * 2; + mHeightPixels = context.getResources().getDisplayMetrics().heightPixels; + + //need to add the Status bar height + Rect rect = new Rect(); + context.getWindow().getDecorView().getWindowVisibleDisplayFrame(rect); + mHeightPixels = rect.bottom; + } + public KeyboardUtil(Activity context, List dataList) { + LinearLayout mIncludeKeyboardview = (LinearLayout) LayoutInflater.from(context).inflate(R.layout.include_pinpad, null); + this.dataList = dataList; + this.mActivity = context; + this.mParent = mIncludeKeyboardview; + + pinpadEditText = mIncludeKeyboardview.findViewById(R.id.pinpadEditText); + mKeyboardView = (MyKeyboardView) mIncludeKeyboardview.findViewById(R.id.keyboard_view); + mWindow = new PopupWindow(mIncludeKeyboardview, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, false); +// mWindow.setAnimationStyle(R.style.AnimBottom); //Animation style + mWindow.setOnDismissListener(mOnDismissListener); + mWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);//prevent being blocked by the bottom toolbar + int mEditTextHeight = dp2px(44);//44dp edit text height + mKeyboardHeight = dp2px(260);//260dp + mKeyBoardMarginEditTextTopHeight = mEditTextHeight * 2; + mHeightPixels = context.getResources().getDisplayMetrics().heightPixels; + initKeyboard(MyKeyboardView.KEYBOARDTYPE_Only_Num_Pwd, pinpadEditText); + + } + + public void initKeyboard(EditText... editTexts) { + initKeyboard(MyKeyboardView.KEYBOARDTYPE_Num_Pwd, editTexts); + } + + /** + * init keyboard + * + * @param keyBoardType keyboard type + * @param editTexts edit text + */ + @SuppressWarnings("all") + public void initKeyboard(final int keyBoardType, EditText... editTexts) { + for (final EditText editText : editTexts) { + hideSystemSofeKeyboard(editText); + show(keyBoardType, editText); + +// editText.setOnTouchListener(new View.OnTouchListener() { +// @Override +// public boolean onTouch(View v, MotionEvent event) { +// if (event.getAction() == MotionEvent.ACTION_UP) { +// show(keyBoardType, editText); +// } +// return false; +// } +// }); + } + } + + /** + * Set edittext that does not need to use this keyboard + * + * @param edittexts + */ + @SuppressWarnings("all") + public void setOtherEdittext(EditText... editTexts) { + for (EditText editText : editTexts) { + editText.setOnTouchListener(new View.OnTouchListener() { + @Override + public boolean onTouch(View v, MotionEvent event) { + if (event.getAction() == MotionEvent.ACTION_UP) { + //prevent situations where the keyboard is not hidden new Handler().postDelayed(new Runnable()) + hide(); + } + return false; + } + }); + } + } + + public void show(int keyBoardType, EditText editText) { + //hide system + KeyboardTool.hideInputForce(mActivity, editText); + //init keyboard + mKeyboardView.setHeight(mHeightPixels); + if (mKeyboardView.getEditText() != editText || needInit) { + mKeyboardView.init(editText, mWindow, keyBoardType, dataList); + } + //display custom keyboard + if (mWindow != null && !mWindow.isShowing()) { + mWindow.showAtLocation(mParent, Gravity.BOTTOM, 0, 0); + } else { +// mWindow = null; + } + //modify the position of the parent control + int nKeyBoardToTopHeight = mHeightPixels - mKeyboardHeight;//screen height-keyboard height + int[] editLocal = new int[2]; + editText.getLocationOnScreen(editLocal); + + ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mParent.getLayoutParams(); + if (editLocal[1] + mKeyBoardMarginEditTextTopHeight > nKeyBoardToTopHeight) { + int height = editLocal[1] - lp.topMargin - nKeyBoardToTopHeight; + int mScrollToValue = height + mKeyBoardMarginEditTextTopHeight; + lp.topMargin = 0 - mScrollToValue; + mParent.setLayoutParams(lp); + mScrollTo = true; + } + +// getLocation(mKeyboardView); + } + + public boolean hide() { + + if (mWindow != null && mWindow.isShowing()) { + mWindow.dismiss(); + needInit = true; + return true; + } + return false; + } + + /** + * hide system keyboard + * + * @param editText + */ + private static void hideSystemSofeKeyboard(EditText editText) { + //SDK_INT >= 11 + try { + Class cls = EditText.class; + Method setShowSoftInputOnFocus; + setShowSoftInputOnFocus = cls.getMethod("setShowSoftInputOnFocus", boolean.class); + setShowSoftInputOnFocus.setAccessible(true); + setShowSoftInputOnFocus.invoke(editText, false); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private int dp2px(float dpValue) { + float scale = mActivity.getResources().getDisplayMetrics().density; + + return (int) (dpValue * scale + 0.5f); + } + + //keyboard dismiss,recover parent control + private PopupWindow.OnDismissListener mOnDismissListener = new PopupWindow.OnDismissListener() { + @Override + public void onDismiss() { + if (mScrollTo) { + mActivity.runOnUiThread(new Runnable() { + @Override + public void run() { + mScrollTo = false; + ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mParent.getLayoutParams(); + lp.topMargin = 0; + mParent.setLayoutParams(lp); + } + }); + } + } + }; + + /* + * The minimum height of the keyboard from the top of the edit text + **/ + public void setKeyBoardMarginEditTextTopHeight(int mKeyBoardMarginEditTextTopHeight) { + this.mKeyBoardMarginEditTextTopHeight = mKeyBoardMarginEditTextTopHeight; + } +} + + diff --git a/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/MyKeyboardView.java b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/MyKeyboardView.java new file mode 100644 index 0000000..b6525a8 --- /dev/null +++ b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/MyKeyboardView.java @@ -0,0 +1,331 @@ +package com.test.cardreadtest.payment.pinkeyboard; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.drawable.Drawable; +import android.inputmethodservice.Keyboard; +import android.inputmethodservice.KeyboardView; +import android.text.Editable; +import android.util.AttributeSet; +import android.util.Log; +import android.widget.EditText; +import android.widget.PopupWindow; + +import com.test.cardreadtest.utils.QPOSUtil; +import com.test.cardreadtest.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * **************************************************************** + * File Name: MyKeyboardView + * File Description: Keyboard View + * **************************************************************** + */ +public class MyKeyboardView extends KeyboardView { + public static final int KEYBOARDTYPE_Num = 0;//Number keyboard + public static final int KEYBOARDTYPE_Num_Pwd = 1;//Number type keyboard(password) + public static final int KEYBOARDTYPE_ABC = 2;//letter keyboard + public static final int KEYBOARDTYPE_Symbol = 4;//symbol keyboard + public static final int KEYBOARDTYPE_Only_Num_Pwd = 5;//only number keyboard + + private final String strLetter = "abcdefghijklmnopqrstuvwxyz";//letter + + private EditText mEditText; + private PopupWindow mWindow; + private Activity mActivity; + + private Keyboard keyboardNum; + private Keyboard keyboardNumPwd; + private Keyboard keyboardOnlyNumPwd; + private Keyboard keyboardABC; + private Keyboard keyboardSymbol; + private int mHeightPixels;//screen height + + public boolean isSupper = false;//whether the letter keyboard is capitalized + public boolean isPwd = false;//whether the numbers on the number keyboard are random + private int keyBoardType;//keyboard type + private List dataList = new ArrayList<>(); + + public MyKeyboardView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public MyKeyboardView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void setHeight(int mHeightPixels) { + this.mHeightPixels = mHeightPixels; + } + + public void setContext(Activity mActivity) { + this.mActivity = mActivity; + } + + public void init(EditText editText, PopupWindow window, int keyBoardType, List dataList) { + this.dataList = dataList; + this.mEditText = editText; + this.mWindow = window; + this.keyBoardType = keyBoardType; + if (keyBoardType == KEYBOARDTYPE_Num_Pwd || keyBoardType == KEYBOARDTYPE_Only_Num_Pwd) { + isPwd = true; + } + setEnabled(true); + setPreviewEnabled(false); + setOnKeyboardActionListener(mOnKeyboardActionListener); + setKeyBoardType(keyBoardType); + } + + public EditText getEditText() { + return mEditText; + } + + /** + * set keyboard type + */ + public void setKeyBoardType(int keyBoardType) { + switch (keyBoardType) { + case KEYBOARDTYPE_Num: + if (keyboardNum == null) { + keyboardNum = new Keyboard(getContext(), R.xml.keyboard_number); + } + setKeyboard(keyboardNum); + break; + case KEYBOARDTYPE_ABC: +// if (keyboardABC == null) { +// keyboardABC = new Keyboard(getContext(), R.xml.keyboard_abc); +// } +// setKeyboard(keyboardABC); + break; + case KEYBOARDTYPE_Num_Pwd: + if (keyboardNumPwd == null) { + keyboardNumPwd = new Keyboard(getContext(), R.xml.keyboard_number); + } + randomKey(keyboardNumPwd); + setKeyboard(keyboardNumPwd); + break; + case KEYBOARDTYPE_Symbol: + if (keyboardSymbol == null) { + keyboardSymbol = new Keyboard(getContext(), R.xml.keyboard_symbol); + } + setKeyboard(keyboardSymbol); + break; + case KEYBOARDTYPE_Only_Num_Pwd: + if (keyboardOnlyNumPwd == null) { + keyboardOnlyNumPwd = new Keyboard(getContext(), R.xml.keyboard_only_number); + } + randomKey(keyboardOnlyNumPwd); + setKeyboard(keyboardOnlyNumPwd); + break; + } + } + + private OnKeyboardActionListener mOnKeyboardActionListener = new OnKeyboardActionListener() { + + @Override + public void onPress(int primaryCode) { +// List keys = keyboardOnlyNumPwd.getKeys(); +// for(int i = 0 ; i < keys.size(); i++){ +// Keyboard.Key key = keys.get(i); +//// key. +// new FancyShowCaseView.Builder(mActivity) +// .focusOn() +// .title("Focus on View") +// .build() +// .show(); +// } + } + + @Override + public void onRelease(int primaryCode) { + + } + + @Override + public void onKey(int primaryCode, int[] keyCodes) { + Editable editable = mEditText.getText(); + int start = mEditText.getSelectionStart(); + switch (primaryCode) { + case Keyboard.KEYCODE_DELETE://go back + if (editable != null && editable.length() > 0) { + if (start > 0) { + editable.delete(start - 1, start); + } + } + break; + case Keyboard.KEYCODE_SHIFT://switch uppercase or lowercase + changeKey(); + setKeyBoardType(KEYBOARDTYPE_ABC); + break; + case Keyboard.KEYCODE_CANCEL:// hide + case Keyboard.KEYCODE_DONE:// confirm + mWindow.dismiss(); + break; + case 123123://switch number keyboard + if (isPwd) { + setKeyBoardType(KEYBOARDTYPE_Num_Pwd); + } else { + setKeyBoardType(KEYBOARDTYPE_Num); + } + break; + case 456456://switch letter keyboard + if (isSupper)//if the current keyboard is uppercase, change to lowercase + { + changeKey(); + } + setKeyBoardType(KEYBOARDTYPE_ABC); + break; + case 789789://switch symbol keyboard + setKeyBoardType(KEYBOARDTYPE_Symbol); + break; + case 666666:// name Delimiter"·" + editable.insert(start, "·"); + break; + default://input symbol + editable.insert(start, "*"); +// editable.insert(start, Character.toString((char) primaryCode)); + } + } + + @Override + public void onText(CharSequence text) { + + } + + @Override + public void swipeLeft() { + + } + + @Override + public void swipeRight() { + + } + + @Override + public void swipeDown() { + + } + + @Override + public void swipeUp() { + + } + }; + + /** + * switch keyboard uppercase or lowercase + */ + private void changeKey() { + List keylist = keyboardABC.getKeys(); + if (isSupper) {// switch uppercase to lowercase + for (Keyboard.Key key : keylist) { + if (key.label != null && strLetter.contains(key.label.toString().toLowerCase())) { + key.label = key.label.toString().toLowerCase(); + key.codes[0] = key.codes[0] + 32; + } + } + } else {// Switch lowercase to uppercase + for (Keyboard.Key key : keylist) { + if (key.label != null && strLetter.contains(key.label.toString().toLowerCase())) { + key.label = key.label.toString().toUpperCase(); + key.codes[0] = key.codes[0] - 32; + } + } + } + isSupper = !isSupper; + } + + public static KeyBoardNumInterface keyBoardNumInterface; + + /** + * random number keyboard + * code 48-57 (0-9) + */ + public void randomKey(Keyboard pLatinKeyboard) { + int[] ayRandomKey = new int[13]; + if(dataList.size() == 0){ + ayRandomKey = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, -3, 0, -4, -5}; +// Random random = new Random(); +// for (int i = 0; i < ayRandomKey.length; i++) { +// int a = random.nextInt(ayRandomKey.length); +// int temp = ayRandomKey[i]; +// ayRandomKey[i] = ayRandomKey[a]; +// ayRandomKey[a] = temp; +// } + }else { + for (int i = 0; i < dataList.size(); i++) { + ayRandomKey[i] = Integer.valueOf(dataList.get(i), 16); + } + } + + List pKeyLis = pLatinKeyboard.getKeys(); + int index = 0; + int sy = mHeightPixels - pLatinKeyboard.getHeight(); +// int sy = mHeightPixels-80*5-8*4;//D20 is 60 and 6,D1000 is 80 and 8 +// Tip.i("sy = "+sy); + StringBuilder s = new StringBuilder(); + for (int i = 0; i < pKeyLis.size(); i++) { +// if(i == 0){ +// sy = mHeightPixels-pKeyLis.get(i).height*5-pKeyLis.get(i).x*6;//calculate interval value +// } + int code = pKeyLis.get(i).codes[0]; + int y = sy + pKeyLis.get(i).y; + int x = pKeyLis.get(i).x; + int rit = x + pKeyLis.get(i).width; + int riby = y + pKeyLis.get(i).height; + String label; + if (code >= 0) {//number value + pKeyLis.get(i).label = ayRandomKey[index] + ""; + pKeyLis.get(i).codes[0] = 48 + ayRandomKey[index]; + String locationStr = QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(ayRandomKey[index])) + QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(x)) + QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(y)) + + QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(rit)) + QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(riby)); + s.append(locationStr); + index++; + } else { + if (code == -3) { + label = QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(13)); + } else if (code == -4) { + label = QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(15)); + } else { + label = QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(14)); + } + String locationStr = label + QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(x)) + QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(y)) + + QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(rit)) + QPOSUtil.byteArray2Hex(QPOSUtil.intToByteArray(riby)); + s.append(locationStr); + } + } + keyBoardNumInterface.getNumberValue(s.toString()); + } + + public static void setKeyBoardListener(KeyBoardNumInterface mkeyBoardNumInterface) { + keyBoardNumInterface = mkeyBoardNumInterface; + } + + @Override + public void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (keyBoardType == KEYBOARDTYPE_Only_Num_Pwd) {//only number keyboard + List keys = getKeyboard().getKeys(); + for (Keyboard.Key key : keys) { + if (key.codes[0] == -5) {//delete button + Drawable dr = getContext().getResources().getDrawable(R.drawable + .keyboard_white); + dr.setBounds(key.x, key.y, key.x + key.width, key.y + key.height); + dr.draw(canvas); + int drawableX = key.x + (key.width - key.icon.getIntrinsicWidth()) / 2; + int drawableY = key.y + (key.height - key.icon.getIntrinsicHeight()) / 2; + key.icon.setBounds(drawableX, drawableY, drawableX + key.icon + .getIntrinsicWidth(), drawableY + key.icon.getIntrinsicHeight()); + key.icon.draw(canvas); + Log.i("test", "drawableX: " + drawableX + " drawableY: " + drawableY); + } +// Log.i("test","x: " +key.x+" y: "+key.y+" wi:"+key.width+" he:"+key.height); + + } + } + } +} diff --git a/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/PinPadDialog.java b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/PinPadDialog.java new file mode 100644 index 0000000..0d17a37 --- /dev/null +++ b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/PinPadDialog.java @@ -0,0 +1,56 @@ +package com.test.cardreadtest.payment.pinkeyboard; + +import android.app.AlertDialog; +import android.content.Context; +import android.view.Gravity; +import android.view.LayoutInflater; +import android.view.View; +import android.view.Window; +import android.widget.LinearLayout; + +import com.test.cardreadtest.R; + + +public class PinPadDialog { + private AlertDialog mDialog; + private Window window; + private Context mContext; + private int mThemeResId; + private View mDialogLayout; + + + public PinPadDialog(Context context) { + + this.mContext = context; +// this.mThemeResId = R.style.dialog_pay_theme; + this.mDialogLayout = LayoutInflater.from(mContext).inflate(R.layout.view_paypass_dialog, null); + mDialog = new AlertDialog.Builder(mContext, mThemeResId).create(); + mDialog.setCancelable(true); + mDialog.show(); + + mDialog.getWindow().setDimAmount(0.4f); + window = mDialog.getWindow(); + window.setLayout(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + window.setContentView(mDialogLayout); + mDialog.setCanceledOnTouchOutside(false); +// window.setWindowAnimations(R.style.dialogOpenAnimation); + window.setGravity(Gravity.BOTTOM); + + } + + + public PinPadView getPayViewPass() { + return mDialogLayout.findViewById(R.id.pay_View); + + } + + + public void dismiss() { + if (mDialog != null && mDialog.isShowing()) { + mDialog.dismiss(); + mDialog = null; + window = null; + } + } + +} diff --git a/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/PinPadView.java b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/PinPadView.java new file mode 100644 index 0000000..0bddd54 --- /dev/null +++ b/app/src/main/java/com/test/cardreadtest/payment/pinkeyboard/PinPadView.java @@ -0,0 +1,282 @@ +package com.test.cardreadtest.payment.pinkeyboard; + +import android.app.Activity; +import android.content.Context; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.EditorInfo; +import android.widget.BaseAdapter; +import android.widget.EditText; +import android.widget.GridView; +import android.widget.RelativeLayout; +import android.widget.TextView; +import android.widget.Toast; + +import androidx.annotation.Nullable; + +import com.test.cardreadtest.R; +import com.mulberry.xpos.QPOSService; +import com.mulberry.xpos.Util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Custom payment password component + */ + +public class PinPadView extends RelativeLayout { + private Activity mContext; + private GridView mGridView; + private String savePwd = ""; + private List listNumber;//1,2,3---0 + private View mPassLayout; + private boolean isRandom; + private EditText mEtinputpin; + private QPOSService pos; + private String pinData = ""; + + public static interface OnPayClickListener { + + void onCencel(); + + void onPaypass(); + + void onConfirm(String password); + + } + + private OnPayClickListener mPayClickListener; + + public void setPayClickListener(QPOSService qPOSService, OnPayClickListener listener) { + pos = qPOSService; + mPayClickListener = listener; + } + + public PinPadView(Context context) { + super(context); + } + + public PinPadView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public PinPadView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + this.mContext = (Activity) context; + initView(); + this.addView(mPassLayout); + } + + private void initView() { + mPassLayout = LayoutInflater.from(mContext).inflate(R.layout.view_paypass_layout, null); + mEtinputpin = mPassLayout.findViewById(R.id.et_inputpin); + mGridView = mPassLayout.findViewById(R.id.gv_pass); + mEtinputpin.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (actionId == EditorInfo.IME_ACTION_UNSPECIFIED + || actionId == EditorInfo.IME_ACTION_DONE) { + pinData = mEtinputpin.getText().toString().trim(); + mPayClickListener.onConfirm(pinData); + return true; + } + return false; + } + }); + initData(); + } + + + /** + * Is isRandom enabled for random numbers + */ + private void initData() { + if (isRandom) { + listNumber = new ArrayList<>(); + listNumber.clear(); + for (int i = 0; i <= 10; i++) { + listNumber.add(i); + } + //This method is to disrupt the order + Collections.shuffle(listNumber); + for (int i = 0; i <= 10; i++) { + if (listNumber.get(i) == 10) { + listNumber.remove(i); + listNumber.add(9, 10); + } + } + listNumber.add(R.mipmap.ic_pay_del0); + listNumber.add(R.mipmap.ic_pay_del0); + listNumber.add(R.mipmap.ic_pay_del0); + listNumber.add(R.mipmap.ic_pay_del0); + } else { + listNumber = new ArrayList<>(); + listNumber.clear(); + for (int i = 1; i <= 9; i++) { + listNumber.add(i); + } + listNumber.add(10); + listNumber.add(0); + listNumber.add(R.mipmap.ic_pay_del0); + + } + mGridView.setAdapter(adapter); + } + + /** + * Adapters for GridView + */ + BaseAdapter adapter = new BaseAdapter() { + @Override + public int getCount() { + return listNumber.size(); + } + + @Override + public Object getItem(int position) { + return listNumber.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + @Override + public View getView(final int position, View convertView, ViewGroup parent) { + final ViewHolder holder; + if (convertView == null) { + convertView = View.inflate(mContext, R.layout.view_paypass_gridview_item, null); + holder = new ViewHolder(); + holder.btnNumber = (TextView) convertView.findViewById(R.id.btNumber); + convertView.setTag(holder); + } else { + holder = (ViewHolder) convertView.getTag(); + } + holder.btnNumber.setText(listNumber.get(position) + ""); + if (position == 10) { + holder.btnNumber.setBackgroundColor(mContext.getResources().getColor(me.goldze.mvvmhabit.R.color.gray)); + } + if (position == 9) { + holder.btnNumber.setText("Delete"); + holder.btnNumber.setTextSize(15); + holder.btnNumber.setBackgroundColor(mContext.getResources().getColor(me.goldze.mvvmhabit.R.color.gray)); + } + if (position == 11) { + holder.btnNumber.setText("Clear"); + holder.btnNumber.setTextSize(15); + holder.btnNumber.setBackgroundResource(listNumber.get(position)); + } + if (position == 12) { + holder.btnNumber.setText("pass"); + holder.btnNumber.setTextSize(15); + holder.btnNumber.setBackgroundColor(mContext.getResources().getColor(me.goldze.mvvmhabit.R.color.gray)); + + } + if (position == 13) { + holder.btnNumber.setText(R.string.select_dialog_cancel); + holder.btnNumber.setTextSize(15); + holder.btnNumber.setBackgroundColor(mContext.getResources().getColor(me.goldze.mvvmhabit.R.color.gray)); + + } + + if (position == 14) { + holder.btnNumber.setText(R.string.select_dialog_confirm); + holder.btnNumber.setTextSize(15); + holder.btnNumber.setBackgroundColor(mContext.getResources().getColor(me.goldze.mvvmhabit.R.color.gray)); + } + + if (position == 11) { + holder.btnNumber.setOnTouchListener(new OnTouchListener() { + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (position == 11) { + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + holder.btnNumber.setBackgroundResource(R.mipmap.ic_pay_del1); + break; + case MotionEvent.ACTION_MOVE: + holder.btnNumber.setBackgroundResource(R.mipmap.ic_pay_del1); + break; + case MotionEvent.ACTION_UP: + holder.btnNumber.setBackgroundResource(R.mipmap.ic_pay_del0); + break; + } + } + return false; + } + }); + } + holder.btnNumber.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (position < 11 && position != 9) { + if (savePwd.length() == 12) { + return; + } else { + String SavePwds = String.valueOf(listNumber.get(position)); + if (pos.getCvmKeyList() != null && !("").equals(pos.getCvmKeyList())) { + String keyList = Util.convertHexToString(pos.getCvmKeyList()); + for (int j = 0; j < keyList.length(); j++) { + if (keyList.charAt(j) == SavePwds.charAt(0)) { + savePwd = savePwd + Integer.toHexString(j); + break; + } + } + } + mEtinputpin.setText(savePwd); + } + } else if (position == 11) { + if (savePwd.length() > 0) { + savePwd = savePwd.substring(0, savePwd.length() - 1); + mEtinputpin.setText(savePwd); + } + } + if (position == 9) { + if (savePwd.length() > 0) { + savePwd = ""; + mEtinputpin.setText(""); + } + } else if (position == 12) {//paypass + mPayClickListener.onPaypass(); + } else if (position == 13) {//cancel + mPayClickListener.onCencel(); + } else if (position == 14) {//confirm + pinData = mEtinputpin.getText().toString().trim(); + if (pinData.length() >= 4 && pinData.length() <= 12) { + mPayClickListener.onConfirm(pinData); + } else { + Toast.makeText(mContext, "The length just can input 4 - 12 digits", Toast.LENGTH_SHORT).show(); + } + } + } + }); + + return convertView; + } + }; + + static class ViewHolder { + public TextView btnNumber; + } + + /*** + * Set random number + * @param israndom + */ + public PinPadView setRandomNumber(boolean israndom) { + isRandom = israndom; + initData(); + adapter.notifyDataSetChanged(); + return this; + } + + +} diff --git a/app/src/main/res/drawable/keyboard_keybg_white.xml b/app/src/main/res/drawable/keyboard_keybg_white.xml new file mode 100644 index 0000000..01b1a6d --- /dev/null +++ b/app/src/main/res/drawable/keyboard_keybg_white.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/keyboard_white.xml b/app/src/main/res/drawable/keyboard_white.xml new file mode 100644 index 0000000..dada932 --- /dev/null +++ b/app/src/main/res/drawable/keyboard_white.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_payment.xml b/app/src/main/res/layout/activity_payment.xml index a218d8d..69a32f2 100644 --- a/app/src/main/res/layout/activity_payment.xml +++ b/app/src/main/res/layout/activity_payment.xml @@ -43,6 +43,8 @@ android:text="@{viewModel.status}" android:textSize="16sp" android:layout_marginBottom="20dp" + android:visibility="@{viewModel.showProgress ? android.view.View.VISIBLE : android.view.View.GONE}" + tools:text="Status: Processing..." /> @@ -51,10 +53,12 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" + android:visibility="@{viewModel.showProgress ? android.view.View.VISIBLE : android.view.View.GONE}" android:layout_marginBottom="20dp" /> + + + + + + + + + + + + +