Safepoint Card Reading
This commit is contained in:
parent
d7617186a6
commit
124da43a1e
@ -45,19 +45,14 @@ android {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
|
||||||
implementation libs.appcompat
|
implementation libs.appcompat
|
||||||
implementation libs.material
|
implementation libs.material
|
||||||
implementation libs.activity
|
implementation libs.activity
|
||||||
implementation libs.constraintlayout
|
implementation libs.constraintlayout
|
||||||
implementation libs.cardview
|
implementation libs.cardview
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
implementation 'androidx.recyclerview:recyclerview:1.3.0'
|
||||||
|
|
||||||
// Existing PayLib
|
|
||||||
// implementation(name: 'PayLib-release-2.0.17', ext: 'aar')
|
|
||||||
|
|
||||||
// Tambahkan dependencies yang kompatibel dari referensi
|
|
||||||
implementation 'com.sunmi:printerlibrary:1.0.15'
|
implementation 'com.sunmi:printerlibrary:1.0.15'
|
||||||
implementation 'org.bouncycastle:bcpkix-jdk15on:1.70'
|
|
||||||
|
|
||||||
// Test dependencies
|
// Test dependencies
|
||||||
testImplementation libs.junit
|
testImplementation libs.junit
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
tools:ignore="QueryAllPackagesPermission" />
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
|
android:name=".MyApplication"
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
android:fullBackupContent="@xml/backup_rules"
|
android:fullBackupContent="@xml/backup_rules"
|
||||||
@ -71,7 +72,8 @@
|
|||||||
android:name=".HistoryDetailActivity"
|
android:name=".HistoryDetailActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".CreditCardActivity"
|
android:name=".kredit.CreditCardActivity"
|
||||||
|
style="@style/Theme.AppCompat"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
27
app/src/main/java/com/example/bdkipoc/CacheHelper.java
Normal file
27
app/src/main/java/com/example/bdkipoc/CacheHelper.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package com.example.bdkipoc;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
public class CacheHelper {
|
||||||
|
|
||||||
|
private static final String PREFERENCE_FILE_NAME = "sm_pay_demo_obj";
|
||||||
|
|
||||||
|
private static final String KEY_LANGUAGE = "key_language";
|
||||||
|
|
||||||
|
public static void saveCurrentLanguage(int language) {
|
||||||
|
SharedPreferences sharedPreferences = MyApplication.app.getSharedPreferences(PREFERENCE_FILE_NAME, Context.MODE_PRIVATE);
|
||||||
|
int value = sharedPreferences.getInt(KEY_LANGUAGE, Constant.LANGUAGE_AUTO);
|
||||||
|
if (value == language) return;
|
||||||
|
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||||
|
editor.putInt(KEY_LANGUAGE, language);
|
||||||
|
editor.apply();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getCurrentLanguage() {
|
||||||
|
SharedPreferences sharedPreferences = MyApplication.app.getSharedPreferences(PREFERENCE_FILE_NAME, Context.MODE_PRIVATE);
|
||||||
|
return sharedPreferences.getInt(KEY_LANGUAGE, Constant.LANGUAGE_AUTO);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
17
app/src/main/java/com/example/bdkipoc/Constant.java
Normal file
17
app/src/main/java/com/example/bdkipoc/Constant.java
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package com.example.bdkipoc;
|
||||||
|
|
||||||
|
public class Constant {
|
||||||
|
|
||||||
|
public static final String TAG = "SDKTestDemo";
|
||||||
|
|
||||||
|
public static final int LANGUAGE_AUTO = 0;
|
||||||
|
public static final int LANGUAGE_ZH_CN = 1;
|
||||||
|
public static final int LANGUAGE_EN_US = 2;
|
||||||
|
public static final int LANGUAGE_JA_JP = 3;
|
||||||
|
|
||||||
|
public static final int SCAN_MODEL_NONE = 100;
|
||||||
|
public static final int SCAN_MODEL_P2Lite = 101;
|
||||||
|
|
||||||
|
public static final String SCAN_MODEL_NONE_VALUE = "NONE";
|
||||||
|
public static final String SCAN_MODEL_P2Lite_VALUE = "P2Lite";
|
||||||
|
}
|
@ -19,6 +19,8 @@ import androidx.core.view.WindowInsetsCompat;
|
|||||||
|
|
||||||
import com.google.android.material.button.MaterialButton;
|
import com.google.android.material.button.MaterialButton;
|
||||||
|
|
||||||
|
import com.example.bdkipoc.kredit.CreditCardActivity;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity {
|
public class MainActivity extends AppCompatActivity {
|
||||||
|
|
||||||
private boolean isExpanded = false; // False = showing only 9 main menus, True = showing all 15 menus
|
private boolean isExpanded = false; // False = showing only 9 main menus, True = showing all 15 menus
|
||||||
@ -158,7 +160,7 @@ public class MainActivity extends AppCompatActivity {
|
|||||||
if (cardView != null) {
|
if (cardView != null) {
|
||||||
cardView.setOnClickListener(v -> {
|
cardView.setOnClickListener(v -> {
|
||||||
if (cardId == R.id.card_kartu_kredit) {
|
if (cardId == R.id.card_kartu_kredit) {
|
||||||
startActivity(new Intent(MainActivity.this, PaymentActivity.class));
|
startActivity(new Intent(MainActivity.this, CreditCardActivity.class));
|
||||||
} else if (cardId == R.id.card_kartu_debit) {
|
} else if (cardId == R.id.card_kartu_debit) {
|
||||||
startActivity(new Intent(MainActivity.this, PaymentActivity.class));
|
startActivity(new Intent(MainActivity.this, PaymentActivity.class));
|
||||||
} else if (cardId == R.id.card_qris) {
|
} else if (cardId == R.id.card_qris) {
|
||||||
|
196
app/src/main/java/com/example/bdkipoc/MyApplication.java
Normal file
196
app/src/main/java/com/example/bdkipoc/MyApplication.java
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
package com.example.bdkipoc;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
import android.app.Service;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.ServiceConnection;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.IBinder;
|
||||||
|
import android.util.DisplayMetrics;
|
||||||
|
|
||||||
|
import com.example.bdkipoc.emv.EmvTTS;
|
||||||
|
import com.example.bdkipoc.utils.LogUtil;
|
||||||
|
import com.example.bdkipoc.utils.Utility;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.emv.EMVOptV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.etc.ETCOptV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadOptV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.print.PrinterOptV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.readcard.ReadCardOptV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.rfid.RFIDOptV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.security.BiometricManagerV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.security.DevCertManagerV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.security.NoLostKeyManagerV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.security.SecurityOptV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.system.BasicOptV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.tax.TaxOptV2;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.test.TestOptV2;
|
||||||
|
import com.sunmi.pay.hardware.wrapper.HCEManagerV2Wrapper;
|
||||||
|
import com.sunmi.peripheral.printer.InnerPrinterCallback;
|
||||||
|
import com.sunmi.peripheral.printer.InnerPrinterException;
|
||||||
|
import com.sunmi.peripheral.printer.InnerPrinterManager;
|
||||||
|
import com.sunmi.peripheral.printer.SunmiPrinterService;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import sunmi.paylib.SunmiPayKernel;
|
||||||
|
|
||||||
|
public class MyApplication extends Application {
|
||||||
|
public static MyApplication app;
|
||||||
|
|
||||||
|
public BasicOptV2 basicOptV2; // 获取基础操作模块
|
||||||
|
public ReadCardOptV2 readCardOptV2; // 获取读卡模块
|
||||||
|
public PinPadOptV2 pinPadOptV2; // 获取PinPad操作模块
|
||||||
|
public SecurityOptV2 securityOptV2; // 获取安全操作模块
|
||||||
|
public EMVOptV2 emvOptV2; // 获取EMV操作模块
|
||||||
|
public TaxOptV2 taxOptV2; // 获取税控操作模块
|
||||||
|
public ETCOptV2 etcOptV2; // 获取ETC操作模块
|
||||||
|
public PrinterOptV2 printerOptV2; // 获取打印操作模块
|
||||||
|
public TestOptV2 testOptV2; // 获取测试操作模块
|
||||||
|
public DevCertManagerV2 devCertManagerV2; // 设备证书操作模块
|
||||||
|
public NoLostKeyManagerV2 noLostKeyManagerV2; // NoLostKey操作模块
|
||||||
|
public HCEManagerV2Wrapper hceV2Wrapper; // HCE操作模块
|
||||||
|
public RFIDOptV2 rfidOptV2; // RFID操作模块
|
||||||
|
public SunmiPrinterService sunmiPrinterService; // 打印模块
|
||||||
|
//public IScanInterface scanInterface; // 扫码模块 (commented out)
|
||||||
|
public BiometricManagerV2 mBiometricManagerV2; // 生物特征模块
|
||||||
|
|
||||||
|
private boolean connectPaySDK;//是否已连接PaySDK
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
app = this;
|
||||||
|
initLocaleLanguage();
|
||||||
|
initEmvTTS();
|
||||||
|
bindPrintService();
|
||||||
|
bindPaySDKService();
|
||||||
|
//bindScannerService(); // Commented out scanner service binding
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void initLocaleLanguage() {
|
||||||
|
Resources resources = app.getResources();
|
||||||
|
DisplayMetrics dm = resources.getDisplayMetrics();
|
||||||
|
Configuration config = resources.getConfiguration();
|
||||||
|
int showLanguage = CacheHelper.getCurrentLanguage();
|
||||||
|
if (showLanguage == Constant.LANGUAGE_AUTO) {
|
||||||
|
LogUtil.e(Constant.TAG, config.locale.getCountry() + "---这是系统语言");
|
||||||
|
config.locale = Resources.getSystem().getConfiguration().locale;
|
||||||
|
} else if (showLanguage == Constant.LANGUAGE_ZH_CN) {
|
||||||
|
LogUtil.e(Constant.TAG, "这是中文");
|
||||||
|
config.locale = Locale.SIMPLIFIED_CHINESE;
|
||||||
|
} else if (showLanguage == Constant.LANGUAGE_EN_US) {
|
||||||
|
LogUtil.e(Constant.TAG, "这是英文");
|
||||||
|
config.locale = Locale.ENGLISH;
|
||||||
|
} else if (showLanguage == Constant.LANGUAGE_JA_JP) {
|
||||||
|
LogUtil.e(Constant.TAG, "这是日文");
|
||||||
|
config.locale = Locale.JAPAN;
|
||||||
|
}
|
||||||
|
resources.updateConfiguration(config, dm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
LogUtil.e(Constant.TAG, "onConfigurationChanged");
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isConnectPaySDK() {
|
||||||
|
return connectPaySDK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bind PaySDK service
|
||||||
|
*/
|
||||||
|
public void bindPaySDKService() {
|
||||||
|
final SunmiPayKernel payKernel = SunmiPayKernel.getInstance();
|
||||||
|
payKernel.initPaySDK(this, new SunmiPayKernel.ConnectCallback() {
|
||||||
|
@Override
|
||||||
|
public void onConnectPaySDK() {
|
||||||
|
LogUtil.e(Constant.TAG, "onConnectPaySDK...");
|
||||||
|
emvOptV2 = payKernel.mEMVOptV2;
|
||||||
|
basicOptV2 = payKernel.mBasicOptV2;
|
||||||
|
pinPadOptV2 = payKernel.mPinPadOptV2;
|
||||||
|
readCardOptV2 = payKernel.mReadCardOptV2;
|
||||||
|
securityOptV2 = payKernel.mSecurityOptV2;
|
||||||
|
taxOptV2 = payKernel.mTaxOptV2;
|
||||||
|
etcOptV2 = payKernel.mETCOptV2;
|
||||||
|
printerOptV2 = payKernel.mPrinterOptV2;
|
||||||
|
testOptV2 = payKernel.mTestOptV2;
|
||||||
|
devCertManagerV2 = payKernel.mDevCertManagerV2;
|
||||||
|
noLostKeyManagerV2 = payKernel.mNoLostKeyManagerV2;
|
||||||
|
mBiometricManagerV2 = payKernel.mBiometricManagerV2;
|
||||||
|
hceV2Wrapper = payKernel.mHCEManagerV2Wrapper;
|
||||||
|
rfidOptV2 = payKernel.mRFIDOptV2;
|
||||||
|
connectPaySDK = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDisconnectPaySDK() {
|
||||||
|
LogUtil.e(Constant.TAG, "onDisconnectPaySDK...");
|
||||||
|
connectPaySDK = false;
|
||||||
|
emvOptV2 = null;
|
||||||
|
basicOptV2 = null;
|
||||||
|
pinPadOptV2 = null;
|
||||||
|
readCardOptV2 = null;
|
||||||
|
securityOptV2 = null;
|
||||||
|
taxOptV2 = null;
|
||||||
|
etcOptV2 = null;
|
||||||
|
printerOptV2 = null;
|
||||||
|
devCertManagerV2 = null;
|
||||||
|
noLostKeyManagerV2 = null;
|
||||||
|
mBiometricManagerV2 = null;
|
||||||
|
rfidOptV2 = null;
|
||||||
|
Utility.showToast(R.string.connect_fail);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bind printer service
|
||||||
|
*/
|
||||||
|
private void bindPrintService() {
|
||||||
|
try {
|
||||||
|
InnerPrinterManager.getInstance().bindService(this, new InnerPrinterCallback() {
|
||||||
|
@Override
|
||||||
|
protected void onConnected(SunmiPrinterService service) {
|
||||||
|
sunmiPrinterService = service;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDisconnected() {
|
||||||
|
sunmiPrinterService = null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (InnerPrinterException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* bind scanner service (commented out)
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
public void bindScannerService() {
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setPackage("com.sunmi.scanner");
|
||||||
|
intent.setAction("com.sunmi.scanner.IScanInterface");
|
||||||
|
bindService(intent, new ServiceConnection() {
|
||||||
|
@Override
|
||||||
|
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||||
|
scanInterface = IScanInterface.Stub.asInterface(service);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onServiceDisconnected(ComponentName name) {
|
||||||
|
scanInterface = null;
|
||||||
|
}
|
||||||
|
}, Service.BIND_AUTO_CREATE);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
private void initEmvTTS() {
|
||||||
|
EmvTTS.getInstance().init();
|
||||||
|
}
|
||||||
|
}
|
145
app/src/main/java/com/example/bdkipoc/emv/EmvTTS.java
Normal file
145
app/src/main/java/com/example/bdkipoc/emv/EmvTTS.java
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
package com.example.bdkipoc.emv;
|
||||||
|
|
||||||
|
import android.speech.tts.TextToSpeech;
|
||||||
|
import android.speech.tts.UtteranceProgressListener;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.example.bdkipoc.MyApplication;
|
||||||
|
import com.example.bdkipoc.utils.LogUtil;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public final class EmvTTS extends UtteranceProgressListener {
|
||||||
|
private static final String TAG = "EmvTTS";
|
||||||
|
private TextToSpeech textToSpeech;
|
||||||
|
private boolean supportTTS;
|
||||||
|
private ITTSProgressListener listener;
|
||||||
|
|
||||||
|
private EmvTTS() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EmvTTS getInstance() {
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTTSListener(ITTSProgressListener l) {
|
||||||
|
listener = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeTTSListener() {
|
||||||
|
listener = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class SingletonHolder {
|
||||||
|
private static final EmvTTS INSTANCE = new EmvTTS();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void init() {
|
||||||
|
//初始化TTS对象
|
||||||
|
destroy();
|
||||||
|
textToSpeech = new TextToSpeech(MyApplication.app, this::onTTSInit);
|
||||||
|
textToSpeech.setOnUtteranceProgressListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void play(String text) {
|
||||||
|
play(text, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void play(String text, String utteranceId) {
|
||||||
|
if (!supportTTS) {
|
||||||
|
Log.e(TAG, "PinPadTTS: play TTS failed, TTS not support...");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (textToSpeech == null) {
|
||||||
|
Log.e(TAG, "PinPadTTS: play TTS slipped, textToSpeech not init..");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log.e(TAG, "play() text: [" + text + "]");
|
||||||
|
textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null, utteranceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart(String utteranceId) {
|
||||||
|
Log.e(TAG, "播放开始,utteranceId:" + utteranceId);
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onStart(utteranceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDone(String utteranceId) {
|
||||||
|
Log.e(TAG, "播放结束,utteranceId:" + utteranceId);
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onDone(utteranceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(String utteranceId) {
|
||||||
|
Log.e(TAG, "播放出错,utteranceId:" + utteranceId);
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onError(utteranceId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop(String utteranceId, boolean interrupted) {
|
||||||
|
Log.e(TAG, "播放停止,utteranceId:" + utteranceId + ",interrupted:" + interrupted);
|
||||||
|
if (listener != null) {
|
||||||
|
listener.onStop(utteranceId, interrupted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void stop() {
|
||||||
|
if (textToSpeech != null) {
|
||||||
|
int code = textToSpeech.stop();
|
||||||
|
Log.e(TAG, "tts stop() code:" + code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isSpeaking() {
|
||||||
|
if (textToSpeech != null) {
|
||||||
|
return textToSpeech.isSpeaking();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void destroy() {
|
||||||
|
if (textToSpeech != null) {
|
||||||
|
textToSpeech.stop();
|
||||||
|
textToSpeech.shutdown();
|
||||||
|
textToSpeech = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** TTS初始化回调 */
|
||||||
|
private void onTTSInit(int status) {
|
||||||
|
if (status != TextToSpeech.SUCCESS) {
|
||||||
|
LogUtil.e(TAG, "PinPadTTS: init TTS failed, status:" + status);
|
||||||
|
supportTTS = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
updateTtsLanguage();
|
||||||
|
if (supportTTS) {
|
||||||
|
textToSpeech.setPitch(1.0f);
|
||||||
|
textToSpeech.setSpeechRate(1.0f);
|
||||||
|
LogUtil.e(TAG, "onTTSInit() success,locale:" + textToSpeech.getVoice().getLocale());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 更新TTS语言 */
|
||||||
|
private void updateTtsLanguage() {
|
||||||
|
Locale locale = Locale.ENGLISH;
|
||||||
|
int result = textToSpeech.setLanguage(locale);
|
||||||
|
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
|
||||||
|
supportTTS = false; //系统不支持当前Locale对应的语音播报
|
||||||
|
LogUtil.e(TAG, "updateTtsLanguage() failed, TTS not support in locale:" + locale);
|
||||||
|
} else {
|
||||||
|
supportTTS = true;
|
||||||
|
LogUtil.e(TAG, "updateTtsLanguage() success, TTS locale:" + locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,57 @@
|
|||||||
|
package com.example.bdkipoc.emv;
|
||||||
|
|
||||||
|
import android.speech.tts.TextToSpeech;
|
||||||
|
|
||||||
|
public interface ITTSProgressListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an utterance "starts" as perceived by the caller. This will
|
||||||
|
* be soon before audio is played back in the case of a {@link TextToSpeech#speak}
|
||||||
|
* or before the first bytes of a file are written to the file system in the case
|
||||||
|
* of {@link TextToSpeech#synthesizeToFile}.
|
||||||
|
*
|
||||||
|
* @param utteranceId The utterance ID of the utterance.
|
||||||
|
*/
|
||||||
|
void onStart(String utteranceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an utterance has successfully completed processing.
|
||||||
|
* All audio will have been played back by this point for audible output, and all
|
||||||
|
* output will have been written to disk for file synthesis requests.
|
||||||
|
* <p>
|
||||||
|
* This request is guaranteed to be called after {@link #onStart(String)}.
|
||||||
|
*
|
||||||
|
* @param utteranceId The utterance ID of the utterance.
|
||||||
|
*/
|
||||||
|
void onDone(String utteranceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an error has occurred during processing. This can be called
|
||||||
|
* at any point in the synthesis process. Note that there might be calls
|
||||||
|
* to {@link #onStart(String)} for specified utteranceId but there will never
|
||||||
|
* be a call to both {@link #onDone(String)} and {@link #onError(String)} for
|
||||||
|
* the same utterance.
|
||||||
|
*
|
||||||
|
* @param utteranceId The utterance ID of the utterance.
|
||||||
|
* @deprecated Use {@link #onError(String, int)} instead
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Use {@link #onError(String, int)} instead
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
void onError(String utteranceId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an utterance has been stopped while in progress or flushed from the
|
||||||
|
* synthesis queue. This can happen if a client calls {@link TextToSpeech#stop()}
|
||||||
|
* or uses {@link TextToSpeech#QUEUE_FLUSH} as an argument with the
|
||||||
|
* {@link TextToSpeech#speak} or {@link TextToSpeech#synthesizeToFile} methods.
|
||||||
|
*
|
||||||
|
* @param utteranceId The utterance ID of the utterance.
|
||||||
|
* @param interrupted If true, then the utterance was interrupted while being synthesized
|
||||||
|
* and its output is incomplete. If false, then the utterance was flushed
|
||||||
|
* before the synthesis started.
|
||||||
|
*/
|
||||||
|
void onStop(String utteranceId, boolean interrupted);
|
||||||
|
}
|
@ -1,3 +1,341 @@
|
|||||||
public class CreditCardActivity {
|
package com.example.bdkipoc.kredit;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.example.bdkipoc.MyApplication;
|
||||||
|
import com.example.bdkipoc.R;
|
||||||
|
import com.example.bdkipoc.utils.ByteUtil;
|
||||||
|
import com.example.bdkipoc.utils.Utility;
|
||||||
|
import com.sunmi.pay.hardware.aidl.AidlConstants.CardType;
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.readcard.CheckCardCallbackV2;
|
||||||
|
|
||||||
|
public class CreditCardActivity extends AppCompatActivity {
|
||||||
|
private TextView tvResult;
|
||||||
|
private Button btnCheckCard;
|
||||||
|
private boolean checkingCard;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
android.util.Log.d("CreditCard", "onCreate called");
|
||||||
|
setContentView(R.layout.activity_credit_card);
|
||||||
|
initView();
|
||||||
|
|
||||||
|
// Check PaySDK status
|
||||||
|
if (MyApplication.app != null) {
|
||||||
|
android.util.Log.d("CreditCard", "MyApplication.app exists");
|
||||||
|
android.util.Log.d("CreditCard", "PaySDK connected: " + MyApplication.app.isConnectPaySDK());
|
||||||
|
android.util.Log.d("CreditCard", "readCardOptV2 null: " + (MyApplication.app.readCardOptV2 == null));
|
||||||
|
} else {
|
||||||
|
android.util.Log.e("CreditCard", "MyApplication.app is null");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initView() {
|
||||||
|
android.util.Log.d("CreditCard", "initView called");
|
||||||
|
|
||||||
|
// Setup Toolbar as ActionBar
|
||||||
|
androidx.appcompat.widget.Toolbar toolbar = findViewById(R.id.toolbar);
|
||||||
|
if (toolbar != null) {
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
if (getSupportActionBar() != null) {
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setTitle(R.string.card_test_credit_card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tvResult = findViewById(R.id.tv_result);
|
||||||
|
btnCheckCard = findViewById(R.id.btn_check_card);
|
||||||
|
|
||||||
|
if (btnCheckCard != null) {
|
||||||
|
android.util.Log.d("CreditCard", "Button found, setting click listener");
|
||||||
|
btnCheckCard.setOnClickListener(v -> {
|
||||||
|
android.util.Log.d("CreditCard", "Button clicked!");
|
||||||
|
switchCheckCard();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
android.util.Log.e("CreditCard", "Button not found!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tvResult != null) {
|
||||||
|
tvResult.setText("Ready to scan card...");
|
||||||
|
android.util.Log.d("CreditCard", "TextView initialized");
|
||||||
|
} else {
|
||||||
|
android.util.Log.e("CreditCard", "TextView not found!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onSupportNavigateUp() {
|
||||||
|
onBackPressed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void switchCheckCard() {
|
||||||
|
android.util.Log.d("CreditCard", "switchCheckCard called, checkingCard: " + checkingCard);
|
||||||
|
try {
|
||||||
|
if (checkingCard) {
|
||||||
|
android.util.Log.d("CreditCard", "Stopping card check");
|
||||||
|
MyApplication.app.readCardOptV2.cancelCheckCard();
|
||||||
|
btnCheckCard.setText(R.string.card_start_check_card);
|
||||||
|
checkingCard = false;
|
||||||
|
} else {
|
||||||
|
android.util.Log.d("CreditCard", "Starting card check");
|
||||||
|
checkCreditCard();
|
||||||
|
checkingCard = true;
|
||||||
|
btnCheckCard.setText(R.string.card_stop_check_card);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
android.util.Log.e("CreditCard", "Error in switchCheckCard: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkCreditCard() {
|
||||||
|
try {
|
||||||
|
// Ensure PaySDK is bound first
|
||||||
|
if (MyApplication.app == null) {
|
||||||
|
tvResult.setText("Error: Application not initialized");
|
||||||
|
android.util.Log.e("CreditCard", "MyApplication.app is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If not connected, try to bind first
|
||||||
|
if (!MyApplication.app.isConnectPaySDK()) {
|
||||||
|
tvResult.setText("Connecting to PaySDK...");
|
||||||
|
android.util.Log.d("CreditCard", "PaySDK not connected, binding service...");
|
||||||
|
MyApplication.app.bindPaySDKService();
|
||||||
|
|
||||||
|
// Wait a bit and retry
|
||||||
|
btnCheckCard.postDelayed(() -> {
|
||||||
|
if (MyApplication.app.isConnectPaySDK() && MyApplication.app.readCardOptV2 != null) {
|
||||||
|
startCardScan();
|
||||||
|
} else {
|
||||||
|
tvResult.setText("Error: Failed to connect to PaySDK");
|
||||||
|
checkingCard = false;
|
||||||
|
btnCheckCard.setText(R.string.card_start_check_card);
|
||||||
|
}
|
||||||
|
}, 2000); // Wait 2 seconds
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MyApplication.app.readCardOptV2 == null) {
|
||||||
|
tvResult.setText("Error: Card reader not initialized");
|
||||||
|
android.util.Log.e("CreditCard", "readCardOptV2 is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
startCardScan();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
android.util.Log.e("CreditCard", "Error in checkCreditCard: " + e.getMessage());
|
||||||
|
tvResult.setText("Error starting card scan: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
private void startCardScan() {
|
||||||
|
try {
|
||||||
|
int cardType = CardType.MAGNETIC.getValue() | CardType.IC.getValue() | CardType.NFC.getValue();
|
||||||
|
tvResult.setText("Starting card scan...\nPlease insert or swipe your card");
|
||||||
|
|
||||||
|
// Log for debugging
|
||||||
|
android.util.Log.d("CreditCard", "Starting checkCard with cardType: " + cardType);
|
||||||
|
|
||||||
|
MyApplication.app.readCardOptV2.checkCard(cardType, mCheckCardCallback, 60);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
android.util.Log.e("CreditCard", "Error in startCardScan: " + e.getMessage());
|
||||||
|
tvResult.setText("Error starting card scan: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final CheckCardCallbackV2 mCheckCardCallback = new CheckCardCallbackV2.Stub() {
|
||||||
|
@Override
|
||||||
|
public void findMagCard(Bundle info) throws RemoteException {
|
||||||
|
runOnUiThread(() -> handleMagCardResult(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findICCard(String atr) throws RemoteException {
|
||||||
|
Bundle info = new Bundle();
|
||||||
|
info.putString("atr", atr);
|
||||||
|
runOnUiThread(() -> handleICCardResult(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findRFCard(String uuid) throws RemoteException {
|
||||||
|
// Handle RF card detection - changed to single parameter
|
||||||
|
Bundle info = new Bundle();
|
||||||
|
info.putString("uuid", uuid);
|
||||||
|
runOnUiThread(() -> handleRFCardResult(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(int code, String message) throws RemoteException {
|
||||||
|
Bundle info = new Bundle();
|
||||||
|
info.putInt("code", code);
|
||||||
|
info.putString("message", message);
|
||||||
|
runOnUiThread(() -> handleErrorResult(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findICCardEx(Bundle info) throws RemoteException {
|
||||||
|
runOnUiThread(() -> handleICCardResult(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findRFCardEx(Bundle info) throws RemoteException {
|
||||||
|
runOnUiThread(() -> handleRFCardResult(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onErrorEx(Bundle info) throws RemoteException {
|
||||||
|
runOnUiThread(() -> handleErrorResult(info));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private void handleMagCardResult(Bundle info) {
|
||||||
|
android.util.Log.d("CreditCard", "=== MAGNETIC CARD DATA ===");
|
||||||
|
|
||||||
|
String track1 = Utility.null2String(info.getString("TRACK1"));
|
||||||
|
String track2 = Utility.null2String(info.getString("TRACK2"));
|
||||||
|
String track3 = Utility.null2String(info.getString("TRACK3"));
|
||||||
|
|
||||||
|
// Log detailed track data
|
||||||
|
android.util.Log.d("CreditCard", "Track1: " + track1);
|
||||||
|
android.util.Log.d("CreditCard", "Track2: " + track2);
|
||||||
|
android.util.Log.d("CreditCard", "Track3: " + track3);
|
||||||
|
|
||||||
|
// Log error codes if available
|
||||||
|
int track1ErrorCode = info.getInt("track1ErrorCode", 0);
|
||||||
|
int track2ErrorCode = info.getInt("track2ErrorCode", 0);
|
||||||
|
int track3ErrorCode = info.getInt("track3ErrorCode", 0);
|
||||||
|
|
||||||
|
android.util.Log.d("CreditCard", "Track1 Error Code: " + track1ErrorCode);
|
||||||
|
android.util.Log.d("CreditCard", "Track2 Error Code: " + track2ErrorCode);
|
||||||
|
android.util.Log.d("CreditCard", "Track3 Error Code: " + track3ErrorCode);
|
||||||
|
|
||||||
|
// Log additional info if available
|
||||||
|
String pan = info.getString("pan", "");
|
||||||
|
String serviceCode = info.getString("servicecode", "");
|
||||||
|
if (!pan.isEmpty()) android.util.Log.d("CreditCard", "PAN: " + pan);
|
||||||
|
if (!serviceCode.isEmpty()) android.util.Log.d("CreditCard", "Service Code: " + serviceCode);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder()
|
||||||
|
.append(getString(R.string.card_mag_card_detected)).append("\n")
|
||||||
|
.append("Track1:").append(track1).append("\n")
|
||||||
|
.append("Track2:").append(track2).append("\n")
|
||||||
|
.append("Track3:").append(track3);
|
||||||
|
tvResult.setText(sb);
|
||||||
|
switchCheckCard();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleICCardResult(Bundle info) {
|
||||||
|
android.util.Log.d("CreditCard", "=== IC CARD DATA ===");
|
||||||
|
|
||||||
|
String atr = info.getString("atr", "");
|
||||||
|
int cardType = info.getInt("cardType", -1);
|
||||||
|
|
||||||
|
android.util.Log.d("CreditCard", "ATR: " + atr);
|
||||||
|
android.util.Log.d("CreditCard", "Card Type: " + cardType);
|
||||||
|
android.util.Log.d("CreditCard", "Full IC Card Data: " + bundleToString(info));
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(getString(R.string.card_ic_card_detected)).append("\n")
|
||||||
|
.append("ATR:").append(atr).append("\n");
|
||||||
|
if (cardType != -1) {
|
||||||
|
sb.append("Card Type:").append(cardType).append("\n");
|
||||||
|
}
|
||||||
|
tvResult.setText(sb);
|
||||||
|
switchCheckCard();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleRFCardResult(Bundle info) {
|
||||||
|
android.util.Log.d("CreditCard", "=== RF/NFC CARD DATA ===");
|
||||||
|
|
||||||
|
String uuid = info.getString("uuid", "");
|
||||||
|
String ats = info.getString("ats", "");
|
||||||
|
int cardType = info.getInt("cardType", -1);
|
||||||
|
int sak = info.getInt("sak", -1);
|
||||||
|
int cardCategory = info.getInt("cardCategory", -1);
|
||||||
|
byte[] atqa = info.getByteArray("atqa");
|
||||||
|
|
||||||
|
android.util.Log.d("CreditCard", "UUID: " + uuid);
|
||||||
|
android.util.Log.d("CreditCard", "ATS: " + ats);
|
||||||
|
android.util.Log.d("CreditCard", "Card Type: " + cardType);
|
||||||
|
android.util.Log.d("CreditCard", "SAK: " + sak);
|
||||||
|
android.util.Log.d("CreditCard", "Card Category: " + cardCategory);
|
||||||
|
if (atqa != null) {
|
||||||
|
android.util.Log.d("CreditCard", "ATQA: " + ByteUtil.bytes2HexStr(atqa));
|
||||||
|
}
|
||||||
|
android.util.Log.d("CreditCard", "Full RF Card Data: " + bundleToString(info));
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("RF Card Detected").append("\n")
|
||||||
|
.append("UUID: ").append(uuid).append("\n");
|
||||||
|
if (!ats.isEmpty()) {
|
||||||
|
sb.append("ATS: ").append(ats).append("\n");
|
||||||
|
}
|
||||||
|
if (sak != -1) {
|
||||||
|
sb.append("SAK: ").append(String.format("0x%02X", sak)).append("\n");
|
||||||
|
}
|
||||||
|
tvResult.setText(sb);
|
||||||
|
switchCheckCard();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleErrorResult(Bundle info) {
|
||||||
|
int code = info.getInt("code");
|
||||||
|
String msg = info.getString("message");
|
||||||
|
String error = "Error: " + msg + " (Code: " + code + ")";
|
||||||
|
tvResult.setText(error);
|
||||||
|
switchCheckCard();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
cancelCheckCard();
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelCheckCard() {
|
||||||
|
try {
|
||||||
|
MyApplication.app.readCardOptV2.cardOff(CardType.NFC.getValue());
|
||||||
|
MyApplication.app.readCardOptV2.cardOff(CardType.IC.getValue());
|
||||||
|
MyApplication.app.readCardOptV2.cancelCheckCard();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to convert Bundle to readable string for logging
|
||||||
|
*/
|
||||||
|
private String bundleToString(Bundle bundle) {
|
||||||
|
if (bundle == null) return "null";
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("{");
|
||||||
|
for (String key : bundle.keySet()) {
|
||||||
|
Object value = bundle.get(key);
|
||||||
|
sb.append(key).append("=");
|
||||||
|
if (value instanceof byte[]) {
|
||||||
|
sb.append(ByteUtil.bytes2HexStr((byte[]) value));
|
||||||
|
} else {
|
||||||
|
sb.append(value);
|
||||||
|
}
|
||||||
|
sb.append(", ");
|
||||||
|
}
|
||||||
|
if (sb.length() > 1) {
|
||||||
|
sb.setLength(sb.length() - 2); // Remove last ", "
|
||||||
|
}
|
||||||
|
sb.append("}");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
265
app/src/main/java/com/example/bdkipoc/utils/ByteUtil.java
Normal file
265
app/src/main/java/com/example/bdkipoc/utils/ByteUtil.java
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
package com.example.bdkipoc.utils;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class ByteUtil {
|
||||||
|
|
||||||
|
/** 打印内容 */
|
||||||
|
public static String byte2PrintHex(byte[] raw, int offset, int count) {
|
||||||
|
if (raw == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (offset < 0 || offset > raw.length) {
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
int end = offset + count;
|
||||||
|
if (end > raw.length) {
|
||||||
|
end = raw.length;
|
||||||
|
}
|
||||||
|
StringBuilder hex = new StringBuilder();
|
||||||
|
for (int i = offset; i < end; i++) {
|
||||||
|
int v = raw[i] & 0xFF;
|
||||||
|
String hv = Integer.toHexString(v);
|
||||||
|
if (hv.length() < 2) {
|
||||||
|
hex.append(0);
|
||||||
|
}
|
||||||
|
hex.append(hv);
|
||||||
|
hex.append(" ");
|
||||||
|
}
|
||||||
|
if (hex.length() > 0) {
|
||||||
|
hex.deleteCharAt(hex.length() - 1);
|
||||||
|
}
|
||||||
|
return hex.toString().toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字节数组转换成16进制字符串
|
||||||
|
*
|
||||||
|
* @param bytes 源字节数组
|
||||||
|
* @return 转换后的16进制字符串
|
||||||
|
*/
|
||||||
|
public static String bytes2HexStr(byte... bytes) {
|
||||||
|
if (bytes == null || bytes.length == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return bytes2HexStr(bytes, 0, bytes.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字节数组转换成16进制字符串
|
||||||
|
*
|
||||||
|
* @param src 源字节数组
|
||||||
|
* @param offset 偏移量
|
||||||
|
* @param len 数据长度
|
||||||
|
* @return 转换后的16进制字符串
|
||||||
|
*/
|
||||||
|
public static String bytes2HexStr(byte[] src, int offset, int len) {
|
||||||
|
int end = offset + len;
|
||||||
|
if (src == null || src.length == 0 || offset < 0 || len < 0 || end > src.length) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
byte[] buffer = new byte[len * 2];
|
||||||
|
int h = 0, l = 0;
|
||||||
|
for (int i = offset, j = 0; i < end; i++) {
|
||||||
|
h = src[i] >> 4 & 0x0f;
|
||||||
|
l = src[i] & 0x0f;
|
||||||
|
buffer[j++] = (byte) (h > 9 ? h - 10 + 'A' : h + '0');
|
||||||
|
buffer[j++] = (byte) (l > 9 ? l - 10 + 'A' : l + '0');
|
||||||
|
}
|
||||||
|
return new String(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] hexStr2Bytes(String hexStr) {
|
||||||
|
if (TextUtils.isEmpty(hexStr)) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
int length = hexStr.length() / 2;
|
||||||
|
char[] chars = hexStr.toCharArray();
|
||||||
|
byte[] b = new byte[length];
|
||||||
|
for (int i = 0; i < length; i++) {
|
||||||
|
b[i] = (byte) (char2Byte(chars[i * 2]) << 4 | char2Byte(chars[i * 2 + 1]));
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte hexStr2Byte(String hexStr) {
|
||||||
|
return (byte) Integer.parseInt(hexStr, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String hexStr2Str(String hexStr) {
|
||||||
|
String vi = "0123456789ABC DEF".trim();
|
||||||
|
char[] array = hexStr.toCharArray();
|
||||||
|
byte[] bytes = new byte[hexStr.length() / 2];
|
||||||
|
int temp;
|
||||||
|
for (int i = 0; i < bytes.length; i++) {
|
||||||
|
char c = array[2 * i];
|
||||||
|
temp = vi.indexOf(c) * 16;
|
||||||
|
c = array[2 * i + 1];
|
||||||
|
temp += vi.indexOf(c);
|
||||||
|
bytes[i] = (byte) (temp & 0xFF);
|
||||||
|
}
|
||||||
|
return new String(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String hexStr2AsciiStr(String hexStr) {
|
||||||
|
String vi = "0123456789ABC DEF".trim();
|
||||||
|
hexStr = hexStr.trim().replace(" ", "").toUpperCase(Locale.US);
|
||||||
|
char[] array = hexStr.toCharArray();
|
||||||
|
byte[] bytes = new byte[hexStr.length() / 2];
|
||||||
|
int temp = 0x00;
|
||||||
|
for (int i = 0; i < bytes.length; i++) {
|
||||||
|
char c = array[2 * i];
|
||||||
|
temp = vi.indexOf(c) << 4;
|
||||||
|
c = array[2 * i + 1];
|
||||||
|
temp |= vi.indexOf(c);
|
||||||
|
bytes[i] = (byte) (temp & 0xFF);
|
||||||
|
}
|
||||||
|
return new String(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将无符号short转换成int,大端模式(高位在前)
|
||||||
|
*/
|
||||||
|
public static int unsignedShort2IntBE(byte[] src, int offset) {
|
||||||
|
return (src[offset] & 0xff) << 8 | (src[offset + 1] & 0xff);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将无符号short转换成int,小端模式(低位在前)
|
||||||
|
*/
|
||||||
|
public static int unsignedShort2IntLE(byte[] src, int offset) {
|
||||||
|
return (src[offset] & 0xff) | (src[offset + 1] & 0xff) << 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将无符号byte转换成int
|
||||||
|
*/
|
||||||
|
public static int unsignedByte2Int(byte[] src, int offset) {
|
||||||
|
return src[offset] & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字节数组转换成int,大端模式(高位在前)
|
||||||
|
*/
|
||||||
|
public static int unsignedInt2IntBE(byte[] src, int offset) {
|
||||||
|
int result = 0;
|
||||||
|
for (int i = offset; i < offset + 4; i++) {
|
||||||
|
result |= (src[i] & 0xff) << (offset + 3 - i) * 8;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字节数组转换成int,小端模式(低位在前)
|
||||||
|
*/
|
||||||
|
public static int unsignedInt2IntLE(byte[] src, int offset) {
|
||||||
|
int value = 0;
|
||||||
|
for (int i = offset; i < offset + 4; i++) {
|
||||||
|
value |= (src[i] & 0xff) << (i - offset) * 8;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将int转换成byte数组,大端模式(高位在前)
|
||||||
|
*/
|
||||||
|
public static byte[] int2BytesBE(int src) {
|
||||||
|
byte[] result = new byte[4];
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
result[i] = (byte) (src >> (3 - i) * 8);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将int转换成byte数组,小端模式(低位在前)
|
||||||
|
*/
|
||||||
|
public static byte[] int2BytesLE(int src) {
|
||||||
|
byte[] result = new byte[4];
|
||||||
|
for (int i = 0; i < 4; i++) {
|
||||||
|
result[i] = (byte) (src >> i * 8);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将short转换成byte数组,大端模式(高位在前)
|
||||||
|
*/
|
||||||
|
public static byte[] short2BytesBE(short src) {
|
||||||
|
byte[] result = new byte[2];
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
result[i] = (byte) (src >> (1 - i) * 8);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将short转换成byte数组,小端模式(低位在前)
|
||||||
|
*/
|
||||||
|
public static byte[] short2BytesLE(short src) {
|
||||||
|
byte[] result = new byte[2];
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
result[i] = (byte) (src >> i * 8);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字节数组列表合并成单个字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] concatByteArrays(byte[]... list) {
|
||||||
|
if (list == null || list.length == 0) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
return concatByteArrays(Arrays.asList(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将字节数组列表合并成单个字节数组
|
||||||
|
*/
|
||||||
|
public static byte[] concatByteArrays(List<byte[]> list) {
|
||||||
|
if (list == null || list.isEmpty()) {
|
||||||
|
return new byte[0];
|
||||||
|
}
|
||||||
|
int totalLen = 0;
|
||||||
|
for (byte[] b : list) {
|
||||||
|
if (b == null || b.length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
totalLen += b.length;
|
||||||
|
}
|
||||||
|
byte[] result = new byte[totalLen];
|
||||||
|
int index = 0;
|
||||||
|
for (byte[] b : list) {
|
||||||
|
if (b == null || b.length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
System.arraycopy(b, 0, result, index, b.length);
|
||||||
|
index += b.length;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert char to byte
|
||||||
|
*
|
||||||
|
* @param c char
|
||||||
|
* @return byte
|
||||||
|
*/
|
||||||
|
private static int char2Byte(char c) {
|
||||||
|
if (c >= 'a') {
|
||||||
|
return (c - 'a' + 10) & 0x0f;
|
||||||
|
}
|
||||||
|
if (c >= 'A') {
|
||||||
|
return (c - 'A' + 10) & 0x0f;
|
||||||
|
}
|
||||||
|
return (c - '0') & 0x0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
89
app/src/main/java/com/example/bdkipoc/utils/LogUtil.java
Normal file
89
app/src/main/java/com/example/bdkipoc/utils/LogUtil.java
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package com.example.bdkipoc.utils;
|
||||||
|
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
public class LogUtil {
|
||||||
|
|
||||||
|
public static final int VERBOSE = 1;
|
||||||
|
public static final int DEBUG = 2;
|
||||||
|
public static final int INFO = 3;
|
||||||
|
public static final int WARN = 4;
|
||||||
|
public static final int ERROR = 5;
|
||||||
|
public static final int NOTHING = 6;
|
||||||
|
public static int LEVEL = VERBOSE;
|
||||||
|
|
||||||
|
public static void setLevel(int Level) {
|
||||||
|
LEVEL = Level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void v(String TAG, String msg) {
|
||||||
|
if (LEVEL <= VERBOSE && !TextUtils.isEmpty(msg)) {
|
||||||
|
MyLog(VERBOSE, TAG, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void d(String TAG, String msg) {
|
||||||
|
if (LEVEL <= DEBUG && !TextUtils.isEmpty(msg)) {
|
||||||
|
MyLog(DEBUG, TAG, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void i(String TAG, String msg) {
|
||||||
|
if (LEVEL <= INFO && !TextUtils.isEmpty(msg)) {
|
||||||
|
MyLog(INFO, TAG, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void w(String TAG, String msg) {
|
||||||
|
if (LEVEL <= WARN && !TextUtils.isEmpty(msg)) {
|
||||||
|
MyLog(WARN, TAG, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void e(String TAG, String msg) {
|
||||||
|
if (LEVEL <= ERROR && !TextUtils.isEmpty(msg)) {
|
||||||
|
MyLog(ERROR, TAG, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void MyLog(int type, String TAG, String msg) {
|
||||||
|
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
|
||||||
|
int index = 4;
|
||||||
|
String className = stackTrace[index].getFileName();
|
||||||
|
String methodName = stackTrace[index].getMethodName();
|
||||||
|
int lineNumber = stackTrace[index].getLineNumber();
|
||||||
|
methodName = methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
|
||||||
|
StringBuilder stringBuilder = new StringBuilder();
|
||||||
|
stringBuilder.append("[ (")
|
||||||
|
.append(className)
|
||||||
|
.append(":")
|
||||||
|
.append(lineNumber)
|
||||||
|
.append(")#")
|
||||||
|
.append(methodName)
|
||||||
|
.append(" ] ");
|
||||||
|
stringBuilder.append(msg);
|
||||||
|
String logStr = stringBuilder.toString();
|
||||||
|
switch (type) {
|
||||||
|
case VERBOSE:
|
||||||
|
Log.v(TAG, logStr);
|
||||||
|
break;
|
||||||
|
case DEBUG:
|
||||||
|
Log.d(TAG, logStr);
|
||||||
|
break;
|
||||||
|
case INFO:
|
||||||
|
Log.i(TAG, logStr);
|
||||||
|
break;
|
||||||
|
case WARN:
|
||||||
|
Log.w(TAG, logStr);
|
||||||
|
break;
|
||||||
|
case ERROR:
|
||||||
|
Log.e(TAG, logStr);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
107
app/src/main/java/com/example/bdkipoc/utils/Utility.java
Normal file
107
app/src/main/java/com/example/bdkipoc/utils/Utility.java
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package com.example.bdkipoc.utils;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.example.bdkipoc.MyApplication;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public final class Utility {
|
||||||
|
private Utility() {
|
||||||
|
throw new AssertionError("Create instance of Utility is forbidden.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Bundle对象转换成字符串 */
|
||||||
|
public static String bundle2String(Bundle bundle) {
|
||||||
|
return bundle2String(bundle, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据key排序后将Bundle内容拼接成字符串
|
||||||
|
*
|
||||||
|
* @param bundle 要处理的bundle
|
||||||
|
* @param order 排序规则,0-不排序,1-升序,2-降序
|
||||||
|
* @return 拼接后的字符串
|
||||||
|
*/
|
||||||
|
public static String bundle2String(Bundle bundle, int order) {
|
||||||
|
if (bundle == null || bundle.keySet().isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
List<String> list = new ArrayList<>(bundle.keySet());
|
||||||
|
if (order == 1) { //升序
|
||||||
|
Collections.sort(list, String::compareTo);
|
||||||
|
} else if (order == 2) {//降序
|
||||||
|
Collections.sort(list, Collections.reverseOrder());
|
||||||
|
}
|
||||||
|
for (String key : list) {
|
||||||
|
sb.append(key);
|
||||||
|
sb.append(":");
|
||||||
|
Object value = bundle.get(key);
|
||||||
|
if (value instanceof byte[]) {
|
||||||
|
sb.append(ByteUtil.bytes2HexStr((byte[]) value));
|
||||||
|
} else {
|
||||||
|
sb.append(value);
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
if (sb.length() > 0) {
|
||||||
|
sb.deleteCharAt(sb.length() - 1);
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 将null转换成空串 */
|
||||||
|
public static String null2String(String str) {
|
||||||
|
return str == null ? "" : str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String formatStr(String format, Object... params) {
|
||||||
|
return String.format(Locale.ENGLISH, format, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** check whether src is hex format */
|
||||||
|
public static boolean checkHexValue(String src) {
|
||||||
|
return Pattern.matches("[0-9a-fA-F]+", src);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 显示Toast */
|
||||||
|
public static void showToast(final String msg) {
|
||||||
|
Handler handler = new Handler(Looper.getMainLooper());
|
||||||
|
handler.post(() -> Toast.makeText(MyApplication.app, msg, Toast.LENGTH_SHORT).show());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 显示Toast */
|
||||||
|
public static void showToast(int resId) {
|
||||||
|
showToast(MyApplication.app.getString(resId));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据结果码获取成功失败信息 */
|
||||||
|
public static String getStateString(int code) {
|
||||||
|
return code == 0 ? "success" : "failed, code:" + code;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 根据结果状态获取成功失败信息 */
|
||||||
|
public static String getStateString(boolean state) {
|
||||||
|
return state ? "success" : "failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 将dp转成px */
|
||||||
|
public static int dp2px(int dp) {
|
||||||
|
float density = MyApplication.app.getResources().getDisplayMetrics().density;
|
||||||
|
return Math.round(dp * density);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 将px转成dp */
|
||||||
|
public static int px2dp(int px) {
|
||||||
|
float density = MyApplication.app.getResources().getDisplayMetrics().density;
|
||||||
|
return Math.round(px / density);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.example.bdkipoc.wrapper;
|
||||||
|
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
|
||||||
|
import com.sunmi.pay.hardware.aidlv2.readcard.CheckCardCallbackV2;
|
||||||
|
|
||||||
|
|
||||||
|
public class CheckCardCallbackV2Wrapper extends CheckCardCallbackV2.Stub {
|
||||||
|
@Override
|
||||||
|
public void findMagCard(Bundle info) throws RemoteException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findICCard(String atr) throws RemoteException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findRFCard(String uuid) throws RemoteException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(int code, String message) throws RemoteException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findICCardEx(Bundle info) throws RemoteException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void findRFCardEx(Bundle info) throws RemoteException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onErrorEx(Bundle info) throws RemoteException {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,120 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical">
|
||||||
android:padding="16dp"
|
|
||||||
tools:context=".CreditCardActivity">
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
android:background="?attr/colorPrimary"
|
||||||
|
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/tvTitle"
|
android:id="@+id/tv_result"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Credit Card Payment"
|
|
||||||
android:textSize="24sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:gravity="center"
|
|
||||||
android:layout_marginBottom="24dp" />
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvConnectionStatus"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="PaySDK Status: Connecting..."
|
|
||||||
android:textSize="16sp"
|
|
||||||
android:padding="12dp"
|
|
||||||
android:background="@drawable/bg_status"
|
|
||||||
android:layout_marginBottom="16dp" />
|
|
||||||
|
|
||||||
<LinearLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="horizontal"
|
|
||||||
android:layout_marginBottom="16dp">
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnCheckStatus"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="Check Status"
|
|
||||||
android:layout_marginEnd="8dp" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnReconnect"
|
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:text="Reconnect"
|
|
||||||
android:layout_marginStart="8dp" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:layout_weight="1">
|
android:layout_weight="1"
|
||||||
|
android:layout_margin="16dp"
|
||||||
|
android:text="Ready to scan card..."
|
||||||
|
android:gravity="top" />
|
||||||
|
|
||||||
<LinearLayout
|
<Button
|
||||||
android:layout_width="match_parent"
|
android:id="@+id/btn_check_card"
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvPaymentMethods"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Payment Methods"
|
|
||||||
android:textSize="18sp"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:layout_marginBottom="12dp" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnReadCard"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Read Card"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:enabled="false" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnEMVPayment"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="EMV Payment"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:enabled="false" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnPinPad"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="PIN Pad Test"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:enabled="false" />
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/btnPrintTest"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Print Test"
|
|
||||||
android:layout_marginBottom="16dp"
|
|
||||||
android:enabled="false" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tvDebugInfo"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="Debug info will appear here..."
|
android:layout_margin="16dp"
|
||||||
android:textSize="12sp"
|
android:text="@string/card_start_check_card" />
|
||||||
android:background="#f5f5f5"
|
|
||||||
android:padding="8dp"
|
|
||||||
android:maxLines="3"
|
|
||||||
android:ellipsize="end" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
@ -1,7 +1,25 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="black">#FF000000</color>
|
<color name="colorPrimary">#3F51B5</color>
|
||||||
<color name="white">#FFFFFFFF</color>
|
<color name="colorPrimaryDark">#303F9F</color>
|
||||||
|
<color name="colorAccent">#FF4081</color>
|
||||||
|
|
||||||
|
<color name="white">#FFFFFF</color>
|
||||||
|
<color name="colorOrange">#FF6600</color>
|
||||||
|
<color name="transparent">#00000000</color>
|
||||||
|
|
||||||
|
<color name="colorBackground">#F0F2F5</color>
|
||||||
|
<color name="colorTextTitle">#222222</color>
|
||||||
|
<color name="colorTextContent">#666666</color>
|
||||||
|
<color name="colorTextHelp">#999999</color>
|
||||||
|
<color name="colorLineColor">#d7d7d7</color>
|
||||||
|
<color name="FD5A52">#FD5A52</color>
|
||||||
|
<color name="CE6E6E6">#E6E6E6</color>
|
||||||
|
|
||||||
|
<color name="FF3C00">#FF3C00</color>
|
||||||
|
<color name="C999999">#999999</color>
|
||||||
|
<color name="black">#000000</color>
|
||||||
|
|
||||||
|
|
||||||
<!-- Banking App Theme Colors -->
|
<!-- Banking App Theme Colors -->
|
||||||
<color name="primary_blue">#1976D2</color>
|
<color name="primary_blue">#1976D2</color>
|
||||||
|
8
app/src/main/res/values/dimens.xml
Normal file
8
app/src/main/res/values/dimens.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<dimen name="smallSize">24dp</dimen>
|
||||||
|
<dimen name="itemSize">48dp</dimen>
|
||||||
|
<dimen name="titleSize">56dp</dimen>
|
||||||
|
|
||||||
|
</resources>
|
@ -13,6 +13,11 @@
|
|||||||
<string name="payment_status_success">Payment Successful!</string>
|
<string name="payment_status_success">Payment Successful!</string>
|
||||||
<string name="return_main">Return to Main Screen</string>
|
<string name="return_main">Return to Main Screen</string>
|
||||||
<string name="main_title">POC</string>
|
<string name="main_title">POC</string>
|
||||||
<!-- In res/values/strings.xml -->
|
<!-- In res/values/strings.xml -->
|
||||||
<string name="connect_fail">Connection to payment service failed</string>
|
<string name="card_test_credit_card">Credit Card Test</string>
|
||||||
|
<string name="card_mag_card_detected">Magnetic Card Detected</string>
|
||||||
|
<string name="card_ic_card_detected">IC Card Detected</string>
|
||||||
|
<string name="card_start_check_card">Start Check Card</string>
|
||||||
|
<string name="card_stop_check_card">Stop Check Card</string>
|
||||||
|
<string name="connect_fail">Connection failed</string>
|
||||||
</resources>
|
</resources>
|
@ -29,6 +29,11 @@
|
|||||||
<item name="titleTextAppearance">@style/ToolbarTitleStyle</item>
|
<item name="titleTextAppearance">@style/ToolbarTitleStyle</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="Toolbar.TitleText" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
|
||||||
|
<item name="android:textSize">18sp</item>
|
||||||
|
<item name="android:textColor">@android:color/white</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<!-- Numpad Button Style -->
|
<!-- Numpad Button Style -->
|
||||||
<style name="NumpadButton">
|
<style name="NumpadButton">
|
||||||
<item name="android:layout_width">0dp</item>
|
<item name="android:layout_width">0dp</item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user