feat(generallibrary): 新增主界面和相关功能模块
- 新增 MainActivity 作为主界面,包含三个底部导航栏选项卡- 新增 BSCardFragment、RechargeFragment 和 MyFragment 作为三个选项卡对应的页面
- 新增 NewCardActivity 用于新建卡片
- 新增 AreaCard 类用于处理区域表号卡数据
- 新增 BaseCard 类作为卡片数据的基础类
- 新增 TabAdapter 用于管理选项卡页面
- 更新 AndroidManifest.xml 设置主活动为 MainActivity
- 更新 build.gradle 添加 generallibrary 依赖
| | |
| | | implementation project(':baselibrary') |
| | | implementation project(':qihealonelibrary') |
| | | implementation project(':qiheonlinelibrary') |
| | | implementation project(':generallibrary') |
| | | |
| | | implementation 'com.tencent.bugly:crashreport:4.1.9.3' |
| | | // 处理图片 |
| | |
| | | android:supportsRtl="true" |
| | | android:theme="@style/AppTheme"> |
| | | |
| | | <!-- <activity--> |
| | | <!-- android:name="com.dayu.recharge.activity.LoginActivity"--> |
| | | <!-- android:exported="true">--> |
| | | <!-- <intent-filter>--> |
| | | <!-- <action android:name="android.intent.action.MAIN" />--> |
| | | <!-- <category android:name="android.intent.category.LAUNCHER" />--> |
| | | <!-- </intent-filter>--> |
| | | <!-- </activity>--> |
| | | <activity |
| | | android:name="com.dayu.recharge.activity.LoginActivity" |
| | | android:name="com.dayu.general.activity.MainActivity" |
| | | android:exported="true"> |
| | | <intent-filter> |
| | | <action android:name="android.intent.action.MAIN" /> |
| | | <category android:name="android.intent.category.LAUNCHER" /> |
| | | </intent-filter> |
| | | </activity> |
| | | |
| | | <meta-data |
| | | android:name="BUGLY_APP_VERSION" |
| | | android:value="7.1" /> |
| | |
| | | public static byte[] companyKey; |
| | | |
| | | /** |
| | | * 密码集合 |
| | | * 扇区都为统一密码时集合 |
| | | */ |
| | | public static List<byte[]> listKeyA = new ArrayList<>(); |
| | | |
| | | /** |
| | | * 每个扇区密码不同时集合 |
| | | */ |
| | | public static List<byte[]> listA_PS=new ArrayList<>(); |
| | | |
| | | //密码a区 |
| | | public static String companyKeyA; |
| | |
| | | public abstract boolean writeData(byte[] str, int a, int b); |
| | | |
| | | |
| | | public abstract boolean writeData(byte[] str, int a, int b,NFCCallBack callBack); |
| | | |
| | | /** |
| | | * 修改密码 |
| | | * |
New file |
| | |
| | | package com.dayu.baselibrary.tools.nfc; |
| | | |
| | | /** |
| | | * NFCCallBack - |
| | | * |
| | | * @author zuoxiao |
| | | * @version 1.0 |
| | | * @since 2025-03-03 |
| | | */ |
| | | public interface NFCCallBack { |
| | | /** |
| | | * 返回是否成功 |
| | | * |
| | | * @param msg |
| | | */ |
| | | void isSusses(boolean flag,String msg); |
| | | } |
| | |
| | | * |
| | | * @return |
| | | */ |
| | | @Override |
| | | public BaseUserCardCard getUserCardData(BaseUserCardCard userCardCard) { |
| | | if (userCardCard!=null){ |
| | | BaseUserCardCard userCard = null; |
| | |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | | public String getCradType() { |
| | | |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | |
| | | * |
| | | * @param callback |
| | | */ |
| | | @Override |
| | | public void getAllData(final NFCCallMapback callback) { |
| | | Map<String, List<byte[]>> map = new HashMap<>(); |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | |
| | | * |
| | | * @param callback |
| | | */ |
| | | @Override |
| | | public void getOneSectorData(NFCCallListback callback) { |
| | | |
| | | |
| | |
| | | * @param b 块 |
| | | * @param callback |
| | | */ |
| | | @Override |
| | | public void getData(final int a, final int b, final NFCCallByteback callback) { |
| | | new Thread(new Runnable() { |
| | | @Override |
| | |
| | | * |
| | | * @return |
| | | */ |
| | | @Override |
| | | public BaseManagerToUserCard getManagerToUserCardData(BaseManagerToUserCard baseManagerToUserCard) { |
| | | |
| | | BaseManagerToUserCard managerToUserCard = null; |
| | |
| | | * @param userCard 用户卡内容 |
| | | * @param |
| | | */ |
| | | @Override |
| | | public boolean writeUserData(BaseUserCardCard userCard) { |
| | | if (userCard != null) { |
| | | int a = 1; |
| | |
| | | * @param b 书写的块(从0开始数) |
| | | * @param |
| | | */ |
| | | @Override |
| | | public boolean writeData(byte[] str, int a, int b) { |
| | | return writeData(str, a, b, null); |
| | | } |
| | | |
| | | @Override |
| | | public boolean writeData(byte[] str, int a, int b, NFCCallBack callBack) { |
| | | Log.i("NFCWreatActivity", "writeData: a=" + a + " b=" + b); |
| | | if (str.length <= 16) { |
| | | try { |
| | |
| | | int count = mfc.getSectorCount(); |
| | | //如果传进来的扇区大了或者小了直接退出方法 |
| | | if (a > count - 1 || a < 0) { |
| | | if (callBack != null) { |
| | | callBack.isSusses(false, "扇区错误--" + a); |
| | | } |
| | | return false; |
| | | } |
| | | //获取写的扇区的块的数量 |
| | | int bCount = mfc.getBlockCountInSector(a); |
| | | //如果输入的块大了或者小了也是直接退出 |
| | | if (b > bCount - 1 || b < 0) { |
| | | if (callBack != null) { |
| | | callBack.isSusses(false, "块区错误--" + b); |
| | | } |
| | | return false; |
| | | } |
| | | //验证扇区密码 |
| | | boolean isOpen = false; |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(0, listKeyA.get(i))) { |
| | | if (listKeyA.size() != 0) { |
| | | for (int i = 0; i < listKeyA.size(); i++) { |
| | | if (mfc.authenticateSectorWithKeyA(0, listKeyA.get(i))) { |
| | | isOpen = true; |
| | | if (listKeyA.get(i).equals(defauleKey)) { |
| | | //当前为默认白卡密码时写卡时修改密码 |
| | | changePasword(a, mfc); |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | } else if (listA_PS.size() != 0 && listA_PS.size() > a) { |
| | | if (mfc.authenticateSectorWithKeyA(0, listA_PS.get(a))) { |
| | | isOpen = true; |
| | | if (listKeyA.get(i).equals(defauleKey)) { |
| | | if (listKeyA.get(a).equals(defauleKey)) { |
| | | //当前为默认白卡密码时写卡时修改密码 |
| | | changePasword(a, mfc); |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | |
| | | |
| | | if (isOpen) { |
| | | int bIndex = mfc.sectorToBlock(a); |
| | | //写卡 |
| | | mfc.writeBlock(bIndex + b, str); |
| | | return true; |
| | | // 校验写入数据是否正确 |
| | | boolean isVerified = true; |
| | | // 读取数据验证 |
| | | byte[] readResult = mfc.readBlock(bIndex + b); |
| | | if (readResult == null) { |
| | | if (callBack != null) { |
| | | callBack.isSusses(false, a + "扇区写卡时再次读取验证时失败"); |
| | | } |
| | | return false; |
| | | } |
| | | for (int i = 0; i < str.length; i++) { |
| | | if (str[i] != readResult[i]) { |
| | | isVerified = false; |
| | | break; |
| | | } |
| | | } |
| | | if (isVerified) { |
| | | if (callBack != null) { |
| | | callBack.isSusses(true, "写入成功并验证通过"); |
| | | } |
| | | return true; |
| | | |
| | | } else { |
| | | if (callBack != null) { |
| | | callBack.isSusses(false, "写入验证失败"); |
| | | } |
| | | return false; |
| | | } |
| | | } |
| | | if (callBack != null) { |
| | | callBack.isSusses(false, a + "扇区密码错误"); |
| | | } |
| | | return false; |
| | | } catch (Exception e) { |
| | | e.printStackTrace(); |
| | | if (callBack != null) { |
| | | callBack.isSusses(false, a + "扇区写卡报错" + e.getMessage()); |
| | | |
| | | } |
| | | return false; |
| | | } finally { |
| | | try { |
| | |
| | | * @param a 书写的扇区 |
| | | * // * @param callback 返回监听 |
| | | */ |
| | | @Override |
| | | public boolean changePasword(int a, MifareClassic mfc) { |
| | | |
| | | byte[] data = new byte[16]; |
| | | if (null != mfc) { |
| | | try { |
| | | |
| | | //将密码转换为keyA |
| | | byte[] dataA = HexUtil.hexToByteArray(companyKeyA); |
| | | for (int i = 0; i < dataA.length; i++) { |
| | |
| | | * |
| | | * @return |
| | | */ |
| | | @Override |
| | | public boolean initCard() { |
| | | try { |
| | | MifareClassic mfc = MifareClassic.get(tag); |
| | |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | */ |
| | | public class NfcWriteAdapter extends BaseNfcWriteHelper { |
| | | NativeNfcWriteHelper nativeNfcWriteHelper; |
| | | |
| | | public NfcWriteAdapter(Intent intent, Activity activity) { |
| | | switch (BaseNfcActivity.adapterType){ |
| | | switch (BaseNfcActivity.adapterType) { |
| | | case ModelUtils.defaultType: |
| | | nativeNfcWriteHelper=new NativeNfcWriteHelper(intent,activity); |
| | | nativeNfcWriteHelper = new NativeNfcWriteHelper(intent, activity); |
| | | break; |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public boolean writeUserData(BaseUserCardCard userCard) { |
| | | switch (BaseNfcActivity.adapterType){ |
| | | switch (BaseNfcActivity.adapterType) { |
| | | case ModelUtils.defaultType: |
| | | return nativeNfcWriteHelper.writeUserData(userCard); |
| | | return nativeNfcWriteHelper.writeUserData(userCard); |
| | | |
| | | } |
| | | return false; |
| | |
| | | |
| | | @Override |
| | | public boolean writeData(byte[] str, int a, int b) { |
| | | switch (BaseNfcActivity.adapterType){ |
| | | switch (BaseNfcActivity.adapterType) { |
| | | case ModelUtils.defaultType: |
| | | return nativeNfcWriteHelper.writeData(str,a,b); |
| | | return nativeNfcWriteHelper.writeData(str, a, b); |
| | | |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | @Override |
| | | public boolean writeData(byte[] str, int a, int b, NFCCallBack callBack) { |
| | | switch (BaseNfcActivity.adapterType) { |
| | | case ModelUtils.defaultType: |
| | | return nativeNfcWriteHelper.writeData(str, a, b,callBack); |
| | | |
| | | } |
| | | return false; |
| | |
| | | |
| | | @Override |
| | | public boolean changePasword(int a, MifareClassic mfc) { |
| | | switch (BaseNfcActivity.adapterType){ |
| | | switch (BaseNfcActivity.adapterType) { |
| | | case ModelUtils.defaultType: |
| | | return nativeNfcWriteHelper.changePasword(a,mfc); |
| | | return nativeNfcWriteHelper.changePasword(a, mfc); |
| | | |
| | | } |
| | | return false; |
New file |
| | |
| | | # For more information about using CMake with Android Studio, read the |
| | | # documentation: https://d.android.com/studio/projects/add-native-code.html |
| | | |
| | | # Sets the minimum version of CMake required to build the native library. |
| | | |
| | | cmake_minimum_required(VERSION 3.4.1) |
| | | |
| | | # Creates and names a library, sets it as either STATIC |
| | | # or SHARED, and provides the relative paths to its source code. |
| | | # You can define multiple libraries, and CMake builds them for you. |
| | | # Gradle automatically packages shared libraries with your APK. |
| | | |
| | | add_library( # Sets the name of the library. |
| | | general-native-lib |
| | | |
| | | # Sets the library as a shared library. |
| | | SHARED |
| | | |
| | | # Provides a relative path to your source file(s). |
| | | src/main/cpp/general-native-lib.cpp ) |
| | | |
| | | # Searches for a specified prebuilt library and stores the path as a |
| | | # variable. Because CMake includes system libraries in the search path by |
| | | # default, you only need to specify the name of the public NDK library |
| | | # you want to add. CMake verifies that the library exists before |
| | | # completing its build. |
| | | |
| | | find_library( # Sets the name of the path variable. |
| | | log-lib |
| | | |
| | | # Specifies the name of the NDK library that |
| | | # you want CMake to locate. |
| | | log ) |
| | | |
| | | # Specifies libraries CMake should link to your target library. You |
| | | # can link multiple libraries, such as libraries you define in this |
| | | # build script, prebuilt third-party libraries, or system libraries. |
| | | |
| | | target_link_libraries( # Specifies the target library. |
| | | general-native-lib |
| | | |
| | | # Links the target library to the log library |
| | | # included in the NDK. |
| | | ${log-lib} ) |
| | |
| | | compileSdk 34 |
| | | |
| | | defaultConfig { |
| | | minSdk 24 |
| | | minSdk 23 |
| | | |
| | | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" |
| | | consumerProguardFiles "consumer-rules.pro" |
| | |
| | | kotlinOptions { |
| | | jvmTarget = '1.8' |
| | | } |
| | | dataBinding { |
| | | enabled = true; |
| | | } |
| | | viewBinding { |
| | | enabled = true; |
| | | } |
| | | externalNativeBuild { |
| | | cmake { |
| | | path "CMakeLists.txt" |
| | | } |
| | | } |
| | | } |
| | | |
| | | dependencies { |
| | | |
| | | implementation project(":baselibrary") |
| | | implementation 'androidx.core:core-ktx:1.15.0' |
| | | implementation 'androidx.appcompat:appcompat:1.7.0' |
| | | implementation 'com.google.android.material:material:1.12.0' |
| | | implementation 'androidx.core:core-ktx:1.12.0' |
| | | implementation 'androidx.appcompat:appcompat:1.6.1' |
| | | implementation 'com.google.android.material:material:1.11.0' |
| | | testImplementation 'junit:junit:4.13.2' |
| | | androidTestImplementation 'androidx.test.ext:junit:1.2.1' |
| | | androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' |
| | |
| | | implementation 'com.pnikosis:materialish-progress:1.7' |
| | | |
| | | //列表 |
| | | compileOnly 'io.github.scwang90:refresh-layout-kernel:2.0.5' |
| | | compileOnly 'io.github.scwang90:refresh-header-classics:2.0.5' |
| | | compileOnly 'androidx.recyclerview:recyclerview:1.2.0'//经典刷新头 |
| | | implementation 'io.github.scwang90:refresh-layout-kernel:2.0.5' |
| | | implementation 'io.github.scwang90:refresh-header-classics:2.0.5' |
| | | implementation 'androidx.recyclerview:recyclerview:1.3.2' |
| | | |
| | | //建议添加 ViewPager2 的明确依赖 |
| | | implementation "androidx.viewpager2:viewpager2:1.1.0-beta01" |
| | | implementation "androidx.fragment:fragment:1.6.2" |
| | | implementation files('../baselibrary/libs/avi.library-2.1.3.aar') |
| | | |
| | | implementation 'com.tencent.bugly:crashreport:4.1.9.3' |
| | | } |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
| | | <manifest xmlns:android="http://schemas.android.com/apk/res/android" |
| | | package="com.dayu.henanlibrary"> |
| | | |
| | | <uses-permission android:name="android.permission.NFC" /> |
| | | <!--用于访问网络,网络定位需要上网--> |
| | | <uses-permission android:name="android.permission.INTERNET" /> |
| | | <uses-permission android:name="android.permission.BLUETOOTH" /> |
| | | <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |
| | | <!-- 要求当前设备必须要有NFC芯片 --> |
| | | <uses-feature |
| | | android:name="android.hardware.nfc" |
| | | android:required="true" /> |
| | | |
| | | <!-- <intent-filter>--> |
| | | <!-- <action android:name="android.nfc.action.TAG_DISCOVERED" />--> |
| | | <!-- <data android:mimeType="text/plain" />--> |
| | | <!-- </intent-filter>--> |
| | | <!--用于进行网络定位--> |
| | | <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission> |
| | | <!--用于访问GPS定位--> |
| | | <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission> |
| | | <!--用于获取运营商信息,用于支持提供运营商信息相关的接口--> |
| | | <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> |
| | | <!--用于访问wifi网络信息,wifi信息会用于进行网络定位--> |
| | | <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission> |
| | | <!--用于获取wifi的获取权限,wifi信息会用来进行网络定位--> |
| | | <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission> |
| | | <!--用于读取手机当前的状态--> |
| | | <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> |
| | | <!--用于写入缓存数据到扩展存储卡--> |
| | | <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> |
| | | <!--用于申请调用A-GPS模块--> |
| | | <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"></uses-permission> |
| | | |
| | | <application |
| | | android:allowBackup="true" |
| | | android:label="@string/app_name" |
| | | android:supportsRtl="true" |
| | | android:theme="@style/AppTheme"> |
| | | |
| | | <!-- <activity--> |
| | | <!-- android:name="com.dayu.recharge.activity.LoginActivity"--> |
| | | <!-- android:exported="true">--> |
| | | <!-- <intent-filter>--> |
| | | <!-- <action android:name="android.intent.action.MAIN" />--> |
| | | <!-- <category android:name="android.intent.category.LAUNCHER" />--> |
| | | <!-- </intent-filter>--> |
| | | <!-- </activity>--> |
| | | <activity android:name="com.dayu.general.activity.NewCardActivity" /> |
| | | |
| | | |
| | | <meta-data |
| | | android:name="BUGLY_APP_VERSION" |
| | | android:value="7.1" /> |
| | | </application> |
| | | |
| | | </manifest> |
New file |
| | |
| | | #include <jni.h> |
| | | #include <string> |
| | | #include <android/log.h> |
| | | #include <string.h> |
| | | #include <jni.h> |
| | | |
| | | static jclass contextClass; |
| | | static jclass signatureClass; |
| | | static jclass packageNameClass; |
| | | static jclass packageInfoClass; |
| | | |
| | | const char *RELEASE_SIGN = "308202b8308201a0020101300d06092a864886f70d01010b05003022310f300d06035504030c06e5a4a7e7a6b9310f300d06035504070c06e5a4a9e6b4a5301e170d3233313132303035333131325a170d3438313131333035333131325a3022310f300d06035504030c06e5a4a7e7a6b9310f300d06035504070c06e5a4a9e6b4a530820122300d06092a864886f70d01010105000382010f003082010a0282010100a0924f3d618e4a622def691e16e54ce5bdfd035bd73e7cb947d2bf3bd0c00afa26e52963e0299fc06d76d153be696c5285d630577e1dcb2b740a72b6d904482217de308fb91c8435441ed05e844ced1e5c3446d82cb8f38751049df26a42adcfc33f1f12c2ce03f676e5d148aad800ace89670b87835e2c02a8570a0b6740d9c0669d4cb3c597d0b2dd49fc0904e885773b6d3a87d9f1e73eb526e0d1a9e9e3c48d986938286cd824151b5a6214faf89d3e699524511b23c86d3b110a7f0bb56a6d2436f69816538a62a38cb1fee6eb685d267cc200df8af51b936bd280beaa2023f75678d77a11ac6de734b30af63d394c8b63bccf2115a47ea15c9212c740d0203010001300d06092a864886f70d01010b05000382010100307cafa9b14be91ba6424cfcc6aed75b069a1c4d6eb646eab0de93f372f236f5f0a6097499df99391075d6ced18d419a2b15adb041890e2b56a3bfbd6be40efee99c5c713ba8ea1d45da09b67916106116e96eb735271c4d53e0739f753145cbc42e149ad3d9507d422ec1c6f1a7f792a4542f9a64f0de3d4f4af69f0fb3390ef3577dcf8844cf744426d173b0934d879148062c5ca64022dc99af370dbfeaf2b5d4a279b20c54a361bca12c25bf185c2885519bbbc36e46ddb083080f0cc5b1f2eafe964ebce5071b0ae7d92a34a9193861b996d2c0299b1993f41063a27038199365a6e3cb27a02ffa9facdc48a63713eb5fbf90e9fd73056aba16b28e5fee"; |
| | | |
| | | |
| | | // 加密密钥 |
| | | const unsigned char XOR_KEY[] = {0x7A, 0xB3, 0xC9, 0xD5, 0xE1, 0xF8, 0x42, 0x91, 0x37, 0x8F, 0x5D, |
| | | 0x2E}; |
| | | |
| | | // 加密后的M1卡密码数组 |
| | | const unsigned char ENCRYPTED_SECTOR_KEYS[] = { |
| | | // 扇区0密码 (加密后) |
| | | 0x9C, 0x45, 0x36, 0x55, 0x8C, 0x7F, 0x35, 0x93, 0x4B, 0x78, 0x2A, 0x79, |
| | | // 扇区1密码 (加密后) |
| | | 0xB4, 0x52, 0x91, 0x45, 0x99, 0x87, 0x3C, 0xA3, 0x6E, 0x5F, 0x1D, 0x5E, |
| | | // 以下类似格式存储其他扇区密码... |
| | | 0x1E, 0x85, 0xEB, 0x51, 0xB9, 0x39, 0x02, 0x2E, 0x77, 0x1F, 0x3E, 0x93, |
| | | 0x22, 0x62, 0xBD, 0x83, 0x99, 0x2A, 0x2F, 0x19, 0x95, 0x07, 0x29, 0x2E, |
| | | 0x90, 0x18, 0xE0, 0x6A, 0xB7, 0x68, 0x34, 0x89, 0x42, 0x5B, 0x16, 0x47, |
| | | 0xA8, 0x0B, 0xB5, 0x97, 0xB4, 0x43, 0x1D, 0x62, 0x89, 0x73, 0x33, 0x2E, |
| | | 0x24, 0x9D, 0xB7, 0x46, 0x8D, 0x64, 0x49, 0x7A, 0x52, 0x41, 0x1C, 0x91, |
| | | 0x31, 0x78, 0x95, 0x52, 0x95, 0x39, 0x19, 0x4C, 0x81, 0x2A, 0x0D, 0x47, |
| | | 0xF5, 0x64, 0xA7, 0x93, 0xB9, 0x51, 0x27, 0x82, 0x43, 0x68, 0x31, 0x3F, |
| | | 0x55, 0x93, 0xB7, 0x68, 0x97, 0x9A, 0x37, 0x71, 0x52, 0x49, 0x17, 0x1E, |
| | | 0x8E, 0x93, 0xB5, 0x44, 0x8D, 0x42, 0x22, 0x84, 0x95, 0x33, 0x22, 0x93, |
| | | 0x42, 0x82, 0xA3, 0x35, 0x91, 0x33, 0x13, 0x93, 0x71, 0x21, 0x01, 0x71, |
| | | 0x37, 0x9F, 0xA6, 0x68, 0x92, 0x86, 0x46, 0x72, 0x43, 0x62, 0x12, 0x52, |
| | | 0x5D, 0x85, 0x93, 0x86, 0x82, 0x46, 0x31, 0x86, 0x57, 0x48, 0x16, 0x88, |
| | | 0x97, 0x73, 0xB5, 0x47, 0x95, 0x55, 0x36, 0x69, 0x49, 0x58, 0x18, 0x6A, |
| | | 0xEA, 0x46, 0x84, 0x93, 0x82, 0x19, 0x29, 0x91, 0x31, 0x1C, 0x0C, 0x7D |
| | | }; |
| | | |
| | | // 解密函数 |
| | | void decrypt_key(const unsigned char *encrypted, char *decrypted, size_t len) { |
| | | for (size_t i = 0; i < len; i++) { |
| | | decrypted[i] = encrypted[i] ^ XOR_KEY[i % sizeof(XOR_KEY)]; |
| | | } |
| | | decrypted[len] = '\0'; |
| | | } |
| | | |
| | | //extern "C" JNIEXPORT jstring |
| | | // |
| | | //JNICALL |
| | | //Java_com_yglx_testjni_MainActivity_getKey( |
| | | // JNIEnv *env, |
| | | // jobject /* this */) { |
| | | // return env->NewStringUTF(AUTH_KEY); |
| | | //} |
| | | |
| | | JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) { |
| | | |
| | | JNIEnv *env = NULL; |
| | | jint result = -1; |
| | | if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) |
| | | return result; |
| | | |
| | | contextClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/Context")); |
| | | signatureClass = (jclass) env->NewGlobalRef((env)->FindClass("android/content/pm/Signature")); |
| | | packageNameClass = (jclass) env->NewGlobalRef( |
| | | (env)->FindClass("android/content/pm/PackageManager")); |
| | | packageInfoClass = (jclass) env->NewGlobalRef( |
| | | (env)->FindClass("android/content/pm/PackageInfo")); |
| | | |
| | | __android_log_print(ANDROID_LOG_DEBUG, "jw", "sss"); |
| | | return JNI_VERSION_1_4; |
| | | } |
| | | |
| | | |
| | | extern "C" |
| | | JNIEXPORT jstring JNICALL |
| | | Java_com_dayu_general_tool_GeBaseHelper_getM1SectorKeySecure(JNIEnv *env, jobject instance, |
| | | jobject contextObject, |
| | | jint sectorIndex) { |
| | | // 获取包管理器相关方法ID |
| | | jmethodID getPackageManagerId = (env)->GetMethodID(contextClass, "getPackageManager", |
| | | "()Landroid/content/pm/PackageManager;"); |
| | | jmethodID getPackageNameId = (env)->GetMethodID(contextClass, "getPackageName", |
| | | "()Ljava/lang/String;"); |
| | | jmethodID signToStringId = (env)->GetMethodID(signatureClass, "toCharsString", |
| | | "()Ljava/lang/String;"); |
| | | jmethodID getPackageInfoId = (env)->GetMethodID(packageNameClass, "getPackageInfo", |
| | | "(Ljava/lang/String;I)Landroid/content/pm/PackageInfo;"); |
| | | |
| | | // 获取应用签名信息 |
| | | jobject packageManagerObject = (env)->CallObjectMethod(contextObject, getPackageManagerId); |
| | | jstring packNameString = (jstring) (env)->CallObjectMethod(contextObject, getPackageNameId); |
| | | jobject packageInfoObject = (env)->CallObjectMethod(packageManagerObject, getPackageInfoId, |
| | | packNameString, 64); |
| | | jfieldID signaturefieldID = (env)->GetFieldID(packageInfoClass, "signatures", |
| | | "[Landroid/content/pm/Signature;"); |
| | | jobjectArray signatureArray = (jobjectArray) (env)->GetObjectField(packageInfoObject, |
| | | signaturefieldID); |
| | | jobject signatureObject = (env)->GetObjectArrayElement(signatureArray, 0); |
| | | |
| | | // 获取签名字符串 |
| | | const char *signStrng = (env)->GetStringUTFChars( |
| | | (jstring) (env)->CallObjectMethod(signatureObject, signToStringId), 0); |
| | | |
| | | // 调试日志输出 |
| | | __android_log_print(ANDROID_LOG_DEBUG, "M1Card", "Current signature: %s", signStrng); |
| | | |
| | | // 验证签名并返回所有扇区密码 |
| | | if (strcmp(signStrng, RELEASE_SIGN) == 0) { |
| | | size_t numKeys = sizeof(ENCRYPTED_SECTOR_KEYS) / 12; // 每个密钥12字节 |
| | | char *allKeys = new char[numKeys * 13]; // 为每个密钥预留12个字符+1个分隔符 |
| | | allKeys[0] = '\0'; |
| | | |
| | | for (size_t i = 0; i < numKeys; i++) { |
| | | char decrypted[13]; |
| | | decrypt_key(ENCRYPTED_SECTOR_KEYS + (i * 12), decrypted, 12); |
| | | |
| | | strcat(allKeys, decrypted); |
| | | if (i < numKeys - 1) { |
| | | strcat(allKeys, ","); |
| | | } |
| | | } |
| | | |
| | | jstring result = (env)->NewStringUTF(allKeys); |
| | | delete[] allKeys; |
| | | return result; |
| | | } else { |
| | | return (env)->NewStringUTF("signature_verification_failed"); |
| | | } |
| | | } |
| | | |
| | | |
| | |
| | | package com.dayu.general.activity |
| | | |
| | | import android.content.Intent |
| | | import android.os.Bundle |
| | | import android.view.LayoutInflater |
| | | import android.view.View |
| | | import android.view.ViewGroup |
| | | import androidx.fragment.app.Fragment |
| | | import com.dayu.general.databinding.FragmentCardBinding |
| | | import com.tencent.bugly.proguard.v |
| | | |
| | | class BSCardFragment :Fragment() { |
| | | |
| | | private var binding: FragmentCardBinding? = null |
| | | override fun onCreate(savedInstanceState: Bundle?) { |
| | | super.onCreate(savedInstanceState) |
| | | } |
| | | |
| | | override fun onCreateView( |
| | | inflater: LayoutInflater, |
| | | container: ViewGroup?, |
| | | savedInstanceState: Bundle? |
| | | ): View? { |
| | | binding = FragmentCardBinding.inflate(LayoutInflater.from(context), container, false) |
| | | initView() |
| | | return binding?.root |
| | | } |
| | | |
| | | private fun initView() { |
| | | binding?.homeNewCard?.setOnClickListener { |
| | | val intent = Intent(context, NewCardActivity::class.java) |
| | | startActivity(intent) |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | |
| | | override fun onCreate(savedInstanceState: Bundle?) { |
| | | super.onCreate(savedInstanceState) |
| | | asynchBaseDao=BaseDaoSingleton.getAsynchInstance(this); |
| | | // asynchBaseDao=BaseDaoSingleton.getAsynchInstance(this); |
| | | } |
| | | |
| | | |
| | |
| | | package com.dayu.general.activity |
| | | |
| | | import android.os.Bundle |
| | | import android.view.LayoutInflater |
| | | import androidx.core.content.ContextCompat |
| | | import androidx.fragment.app.Fragment |
| | | import com.dayu.general.R |
| | | import com.dayu.general.adapter.TabAdapter |
| | | import com.dayu.general.databinding.ActivityMainBinding |
| | | |
| | | class MainActivity : BaseActivity(){ |
| | | class MainActivity : BaseActivity() { |
| | | |
| | | |
| | | var binding: ActivityMainBinding? = null |
| | | private val fragments: ArrayList<Fragment> = ArrayList() |
| | | override fun onCreate(savedInstanceState: Bundle?) { |
| | | super.onCreate(savedInstanceState) |
| | | setupFragments(); |
| | | binding = ActivityMainBinding.inflate(LayoutInflater.from(this)) |
| | | setContentView(binding?.root) |
| | | setupFragments() |
| | | initView() |
| | | initTab() |
| | | |
| | | |
| | | } |
| | | |
| | | |
| | | private fun initView() { |
| | | binding!!.BSCardLL.setOnClickListener { v -> changeBottomState(Tab.BSC) } |
| | | binding!!.rechargeLL.setOnClickListener { v -> changeBottomState(Tab.RECHARGE) } |
| | | binding!!.myLL.setOnClickListener { v -> changeBottomState(Tab.MY) } |
| | | } |
| | | |
| | | |
| | | private fun setupFragments() { |
| | | fragments.add(BSCardFragment()) |
| | | fragments.add(RechargeFragment()) |
| | | fragments.add(BSCardFragment()) |
| | | fragments.add(MyFragment()) |
| | | } |
| | | |
| | | private fun initTab() { |
| | | binding?.viewPager?.adapter = TabAdapter(this, fragments) |
| | | binding?.viewPager?.currentItem = (1) |
| | | binding?.viewPager?.offscreenPageLimit = 3 |
| | | binding?.viewPager?.isUserInputEnabled = false |
| | | } |
| | | |
| | | private enum class Tab { |
| | | BSC, RECHARGE, MY |
| | | } |
| | | |
| | | /** |
| | | * 修改底部状态 |
| | | */ |
| | | private fun changeBottomState(tab: Tab) { |
| | | resetTabState() |
| | | when (tab) { |
| | | Tab.BSC -> updateTabUI(0, R.drawable.bottom_card_white, R.color.white) |
| | | Tab.RECHARGE -> updateTabUI(1, R.drawable.bottom_recharge_white, R.color.white) |
| | | Tab.MY -> updateTabUI(2, R.drawable.bottom_my_white, R.color.white) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 重置所有 Tab 的默认状态 |
| | | */ |
| | | private fun resetTabState() { |
| | | binding!!.BSCardImg.setImageDrawable( |
| | | ContextCompat.getDrawable( |
| | | this, |
| | | R.drawable.bottom_card_black |
| | | ) |
| | | ) |
| | | binding!!.BSCardText.setTextColor(ContextCompat.getColor(this, R.color.black)) |
| | | |
| | | binding!!.rechargeImg.setImageDrawable( |
| | | ContextCompat.getDrawable( |
| | | this, |
| | | R.drawable.bottom_recharge_black |
| | | ) |
| | | ) |
| | | binding!!.rechargeText.setTextColor(ContextCompat.getColor(this, R.color.black)) |
| | | |
| | | binding!!.myImg.setImageDrawable( |
| | | ContextCompat.getDrawable( |
| | | this, |
| | | R.drawable.bottom_my_black |
| | | ) |
| | | ) |
| | | binding!!.myText.setTextColor(ContextCompat.getColor(this, R.color.black)) |
| | | } |
| | | |
| | | /** |
| | | * 更新某个 Tab 的 UI 状态 |
| | | */ |
| | | private fun updateTabUI(position: Int, iconResId: Int, textColorResId: Int) { |
| | | if (position == 1) { |
| | | binding!!.viewPager.setCurrentItem(position, true) |
| | | } else { |
| | | binding!!.viewPager.setCurrentItem(position, false) |
| | | } |
| | | when (position) { |
| | | 0 -> { |
| | | binding!!.BSCardImg.setImageDrawable(ContextCompat.getDrawable(this, iconResId)) |
| | | binding!!.BSCardText.setTextColor(ContextCompat.getColor(this, textColorResId)) |
| | | } |
| | | |
| | | 1 -> { |
| | | binding!!.rechargeImg.setImageDrawable(ContextCompat.getDrawable(this, iconResId)) |
| | | binding!!.rechargeText.setTextColor(ContextCompat.getColor(this, textColorResId)) |
| | | } |
| | | |
| | | 2 -> { |
| | | binding!!.myImg.setImageDrawable(ContextCompat.getDrawable(this, iconResId)) |
| | | binding!!.myText.setTextColor(ContextCompat.getColor(this, textColorResId)) |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | package com.dayu.general.activity |
| | | |
| | | import android.os.Bundle |
| | | import android.view.LayoutInflater |
| | | import android.view.View |
| | | import android.view.ViewGroup |
| | | import androidx.fragment.app.Fragment |
| | | import com.dayu.general.databinding.FragmentMyBinding |
| | | |
| | | class MyFragment:Fragment() { |
| | | |
| | | |
| | | class MyFragment : Fragment() { |
| | | var binding: FragmentMyBinding? = null; |
| | | override fun onCreateView( |
| | | inflater: LayoutInflater, |
| | | container: ViewGroup?, |
| | | savedInstanceState: Bundle? |
| | | ): View? { |
| | | binding = FragmentMyBinding.inflate(inflater, container, false) |
| | | return binding?.root |
| | | } |
| | | } |
New file |
| | |
| | | package com.dayu.general.activity |
| | | |
| | | import android.os.Bundle |
| | | import android.view.LayoutInflater |
| | | import com.dayu.baselibrary.activity.BaseActivity |
| | | import com.dayu.general.databinding.ActivityNewCardGeBinding |
| | | |
| | | class NewCardActivity:BaseActivity() { |
| | | var binding:ActivityNewCardGeBinding? = null |
| | | override fun onCreate(savedInstanceState: Bundle?) { |
| | | super.onCreate(savedInstanceState) |
| | | binding= ActivityNewCardGeBinding.inflate(LayoutInflater.from(this)) |
| | | setContentView(binding?.root) |
| | | } |
| | | |
| | | } |
| | |
| | | package com.dayu.general.activity |
| | | |
| | | import android.os.Bundle |
| | | import android.view.LayoutInflater |
| | | import android.view.View |
| | | import android.view.ViewGroup |
| | | import androidx.fragment.app.Fragment |
| | | import com.dayu.general.databinding.FragmentRechargeBinding |
| | | |
| | | class RechargeFragment:Fragment() { |
| | | class RechargeFragment : Fragment() { |
| | | var binding: FragmentRechargeBinding? = null |
| | | |
| | | override fun onCreateView( |
| | | inflater: LayoutInflater, |
| | | container: ViewGroup?, |
| | | savedInstanceState: Bundle? |
| | | ): View? { |
| | | binding = FragmentRechargeBinding.inflate(inflater, container, false) |
| | | return binding?.root; |
| | | } |
| | | } |
New file |
| | |
| | | package com.dayu.general.adapter |
| | | |
| | | import androidx.fragment.app.Fragment |
| | | import androidx.fragment.app.FragmentActivity |
| | | import androidx.viewpager2.adapter.FragmentStateAdapter |
| | | |
| | | /** |
| | | * author: zuo |
| | | * Date: 2023/12/19 |
| | | * Time: 17:53 |
| | | * 备注: |
| | | */ |
| | | class TabAdapter( |
| | | fragmentActivity: FragmentActivity, |
| | | private val fragments: List<Fragment> |
| | | ) : FragmentStateAdapter(fragmentActivity) { |
| | | |
| | | override fun createFragment(position: Int): Fragment = fragments[position] |
| | | |
| | | override fun getItemCount(): Int = fragments.size |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.tools.BcdUtil |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import java.io.Serializable |
| | | |
| | | /** |
| | | * 区域表号卡 |
| | | * 用于配置控制器的行政区域号,达到只有区域内用户才能刷卡操作的目的 |
| | | */ |
| | | class AreaCard : BaseCard(), Serializable { |
| | | companion object { |
| | | const val CARD_TYPE = "B0" // 卡类型固定为0xB0 |
| | | const val IDENTIFY_CODE_A0 = 0xA0.toByte() // 识别码A0 |
| | | const val IDENTIFY_CODE_B1 = 0xB1.toByte() // 识别码B1 |
| | | const val IDENTIFY_CODE_C2 = 0xC2.toByte() // 识别码C2 |
| | | const val IDENTIFY_CODE_89 = 0x89.toByte() // 识别码89 |
| | | } |
| | | |
| | | var areaNumber: Int = 0 // 国家行政区域号(12位BCD,精确到村) |
| | | var projectCode: Int = 0 // 项目编码(HEX 1-255) |
| | | |
| | | /** |
| | | * 写卡完成后校验是否写卡成功 |
| | | */ |
| | | fun equalsAreaCard(data: List<ByteArray>?): Boolean { |
| | | if (data == null || data.isEmpty()) { |
| | | return false |
| | | } |
| | | return data[0].contentEquals(getZeroBytes()) |
| | | } |
| | | |
| | | /** |
| | | * 通过byte转bean |
| | | */ |
| | | fun getBean(data: List<ByteArray>): AreaCard? { |
| | | try { |
| | | val areaCard = AreaCard() |
| | | // 解析第0块 |
| | | val zero = data[0] |
| | | |
| | | // 解析国家行政区域号(0-5位) |
| | | val areaCodeBytes = zero.copyOfRange(0, 6) |
| | | areaCard.areaNumber = BcdUtil.bcdToStr(areaCodeBytes).toInt() |
| | | |
| | | // 解析项目编码(6位) |
| | | areaCard.projectCode = HexUtil.get16To10LowHightByBytes(byteArrayOf(zero[6])) |
| | | |
| | | // 验证卡类型和识别码 |
| | | if (HexUtil.byteToHex(zero[8]) != CARD_TYPE || |
| | | zero[9] != IDENTIFY_CODE_A0 || |
| | | zero[10] != IDENTIFY_CODE_B1 || |
| | | zero[11] != IDENTIFY_CODE_C2 || |
| | | zero[12] != IDENTIFY_CODE_89) { |
| | | return null |
| | | } |
| | | |
| | | return areaCard |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | return null |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第0块数据 |
| | | */ |
| | | inner class Zero : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 设置国家行政区域号(BCD格式,6字节,0-5位) |
| | | val areaCodeBytes = BcdUtil.strToBcd(String.format("%012d", areaNumber)) |
| | | System.arraycopy(areaCodeBytes, 0, data, 0, 6) |
| | | |
| | | // 设置项目编码(6位) |
| | | data[6] = projectCode.toByte() |
| | | |
| | | // 设置备用位(7位) |
| | | data[7] = 0x00 |
| | | |
| | | // 设置卡类型(8位) |
| | | data[8] = HexUtil.hexToByte(CARD_TYPE) |
| | | |
| | | // 设置识别码(9-12位) |
| | | data[9] = IDENTIFY_CODE_A0 |
| | | data[10] = IDENTIFY_CODE_B1 |
| | | data[11] = IDENTIFY_CODE_C2 |
| | | data[12] = IDENTIFY_CODE_89 |
| | | |
| | | // 设置备用位(13-14位) |
| | | data[13] = 0x00 |
| | | data[14] = 0x00 |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.dao.AppDatabase |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import com.dayu.general.bean.db.CardData |
| | | import com.dayu.general.dao.AppDataBase |
| | | import com.tencent.bugly.crashreport.CrashReport |
| | | |
| | | open class BaseCard { |
| | | var cardData: String? = null //标识码 |
| | | |
| | | fun setCardData(baseDao: AppDataBase, cardType: String?) { |
| | | try { |
| | | val cardDataBean: CardData = |
| | | baseDao.cardDataDao().findFirst(cardType) |
| | | cardData = if (cardDataBean != null) { |
| | | cardDataBean.cardIdentifying |
| | | } else { |
| | | CardCommon.getDefaultCardData(cardType) |
| | | } |
| | | } catch (e: Exception) { |
| | | CrashReport.postCatchedException(e) |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | | * 前15个字节算术累加和 不含进位 |
| | | * |
| | | * @param data 源数据 |
| | | * @return 16进制 |
| | | */ |
| | | fun getByteSum(data: ByteArray?): Byte { |
| | | if (data != null) { |
| | | var sum = 0 |
| | | for (b in data) { |
| | | sum += b.toInt() and 0xFF // & 0xFF 可以将字节扩展为正整数,避免符号位的影响 |
| | | } |
| | | var hex = HexUtil.get10to16CompleteHex(sum) |
| | | hex = HexUtil.spaceHex(hex) |
| | | val hexArr = hex.split(" ".toRegex()).dropLastWhile { it.isEmpty() } |
| | | .toTypedArray() |
| | | return HexUtil.hexToByte(hexArr[hexArr.size - 1]) |
| | | } |
| | | return 0 |
| | | } |
| | | |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | class CardCommon { |
| | | |
| | | val ERROR: Int = -1 |
| | | //连接中断 提示用户重新连接或提示用户重新靠近卡片 |
| | | |
| | | val ERROR_MOVE: Int = -2 |
| | | |
| | | /** |
| | | * 用户刷卡开泵前 |
| | | */ |
| | | |
| | | val USER_CARD_TYPE_1: String = "A1" |
| | | |
| | | /** |
| | | * 用户刷卡开泵后 |
| | | */ |
| | | |
| | | val USER_CARD_TYPE_2: String = "A8" |
| | | |
| | | /** |
| | | * 用户叠加充值 |
| | | */ |
| | | |
| | | val USER_CARD_TYPE_3: String = "A2" |
| | | |
| | | |
| | | /** |
| | | * 设置区域表号卡 |
| | | */ |
| | | |
| | | val REGION: String = "B0" |
| | | |
| | | |
| | | /** |
| | | * 设置用户电量单价卡 |
| | | */ |
| | | |
| | | val ELECTRIC_PRICE: String = "B1" |
| | | |
| | | |
| | | /** |
| | | * 管理卡 |
| | | */ |
| | | |
| | | val MANAGE_CRAD: String = "B2" |
| | | |
| | | |
| | | /** |
| | | * 清零卡 |
| | | */ |
| | | |
| | | val CLEAN_CARD_TYPE: String = "C8" |
| | | |
| | | |
| | | /** |
| | | * 密码卡 |
| | | */ |
| | | |
| | | val PASS_WORD_CRAD_TYPE: String = "B3" |
| | | |
| | | |
| | | /** |
| | | * 配置黑卡 当用户丢失卡时,需要在对应的控制器把此用户配置为黑户,防止非法用水 |
| | | */ |
| | | |
| | | val BLACK: String = "B4" |
| | | |
| | | |
| | | /** |
| | | * 以下未用到 |
| | | * ***************************************************************************************************************************************** |
| | | */ |
| | | /** |
| | | * 重新注册设备卡 |
| | | */ |
| | | |
| | | val REGISTERED_CARD_TYPE: String = "BA" |
| | | |
| | | /** |
| | | * 删除全部用户卡 |
| | | */ |
| | | |
| | | val CLEAN_ALL_USER_CARD_TYPE: String = "BB" |
| | | |
| | | /** |
| | | * 设置域名卡 |
| | | */ |
| | | |
| | | val DOMAIN_CARD_TYPE: String = "C1" |
| | | |
| | | /** |
| | | * 测试卡 |
| | | */ |
| | | |
| | | val TEST_CARD_TYPE: String = "A4" |
| | | |
| | | /** |
| | | * 配置设备注册信息卡 |
| | | */ |
| | | |
| | | val CONFIGURATION_CARD_TYPE: String = "BC" |
| | | |
| | | /** |
| | | * 配置水泵功率卡 |
| | | */ |
| | | |
| | | val CONFIGURATION_POWER_CARD_TYPE: String = "BD" |
| | | |
| | | /** |
| | | * 获取默认的卡标识 |
| | | * |
| | | * @param cardType 卡片类型 |
| | | * @return 卡内容 |
| | | */ |
| | | fun getDefaultCardData(cardType: String?): String { |
| | | var cardData = "" |
| | | when (cardType) { |
| | | CLEAN_CARD_TYPE -> cardData = "3668F7A30119" |
| | | MANAGE_CRAD, REGISTERED_CARD_TYPE, CLEAN_ALL_USER_CARD_TYPE, TEST_CARD_TYPE, CONFIGURATION_CARD_TYPE, CONFIGURATION_POWER_CARD_TYPE, PASS_WORD_CRAD_TYPE, BLACK, ELECTRIC_PRICE -> cardData = |
| | | "A0B1C289" |
| | | } |
| | | return cardData |
| | | } |
| | | |
| | | companion object { |
| | | fun getDefaultCardData(cardType: String?): String? { |
| | | return getDefaultCardData(cardType) |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import java.io.Serializable |
| | | |
| | | /** |
| | | * 检查卡 |
| | | * 用于刷卡显示控制器基本信息:控制器ID号、水量单位、电量单价,未上报的数据条数等信息 |
| | | */ |
| | | class CheckCard : BaseCard(), Serializable { |
| | | companion object { |
| | | const val CARD_TYPE = "B3" // 卡类型:检查卡 |
| | | const val IDENTIFY_CODE_A0 = 0xA0.toByte() // 识别码A0 |
| | | const val IDENTIFY_CODE_B1 = 0xB1.toByte() // 识别码B1 |
| | | const val IDENTIFY_CODE_C2 = 0xC2.toByte() // 识别码C2 |
| | | const val IDENTIFY_CODE_89 = 0x89.toByte() // 识别码89 |
| | | } |
| | | |
| | | /** |
| | | * 写卡完成后校验是否写卡成功 |
| | | */ |
| | | fun equalsCheckCard(data: List<ByteArray>?): Boolean { |
| | | if (data == null || data.isEmpty()) { |
| | | return false |
| | | } |
| | | return data[0].contentEquals(getZeroBytes()) |
| | | } |
| | | |
| | | /** |
| | | * 通过byte转bean |
| | | */ |
| | | fun getBean(data: List<ByteArray>): CheckCard? { |
| | | try { |
| | | val checkCard = CheckCard() |
| | | // 解析第0块 |
| | | val zero = data[0] |
| | | |
| | | // 验证卡类型(8位) |
| | | if (HexUtil.byteToHex(zero[8]) != CARD_TYPE) { |
| | | return null |
| | | } |
| | | |
| | | // 验证识别码(9-12位) |
| | | if (zero[9] != IDENTIFY_CODE_A0 || |
| | | zero[10] != IDENTIFY_CODE_B1 || |
| | | zero[11] != IDENTIFY_CODE_C2 || |
| | | zero[12] != IDENTIFY_CODE_89) { |
| | | return null |
| | | } |
| | | |
| | | return checkCard |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | return null |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第0块数据 |
| | | */ |
| | | inner class Zero : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 设置备用位(0-7位) |
| | | for (i in 0..7) { |
| | | data[i] = 0x00 |
| | | } |
| | | |
| | | // 设置卡类型(8位) |
| | | data[8] = HexUtil.hexToByte(CARD_TYPE) |
| | | |
| | | // 设置识别码(9-12位) |
| | | data[9] = IDENTIFY_CODE_A0 |
| | | data[10] = IDENTIFY_CODE_B1 |
| | | data[11] = IDENTIFY_CODE_C2 |
| | | data[12] = IDENTIFY_CODE_89 |
| | | |
| | | // 设置备用位(13-14位) |
| | | data[13] = 0x00 |
| | | data[14] = 0x00 |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import java.io.Serializable |
| | | |
| | | /** |
| | | * 清零卡 |
| | | * 主要用于内部生产调试用 |
| | | */ |
| | | class ClearCard : BaseCard(), Serializable { |
| | | companion object { |
| | | const val CARD_TYPE = "C1" // 卡类型:清零卡 |
| | | const val IDENTIFY_CODE_A0 = 0xA0.toByte() // 识别码A0 |
| | | const val IDENTIFY_CODE_B1 = 0xB1.toByte() // 识别码B1 |
| | | const val IDENTIFY_CODE_C2 = 0xC2.toByte() // 识别码C2 |
| | | const val IDENTIFY_CODE_89 = 0x89.toByte() // 识别码89 |
| | | } |
| | | |
| | | /** |
| | | * 写卡完成后校验是否写卡成功 |
| | | */ |
| | | fun equalsClearCard(data: List<ByteArray>?): Boolean { |
| | | if (data == null || data.isEmpty()) { |
| | | return false |
| | | } |
| | | return data[0].contentEquals(getZeroBytes()) |
| | | } |
| | | |
| | | /** |
| | | * 通过byte转bean |
| | | */ |
| | | fun getBean(data: List<ByteArray>): ClearCard? { |
| | | try { |
| | | val clearCard = ClearCard() |
| | | // 解析第0块 |
| | | val zero = data[0] |
| | | |
| | | // 验证卡类型(8位) |
| | | if (HexUtil.byteToHex(zero[8]) != CARD_TYPE) { |
| | | return null |
| | | } |
| | | |
| | | // 验证识别码(9-12位) |
| | | if (zero[9] != IDENTIFY_CODE_A0 || |
| | | zero[10] != IDENTIFY_CODE_B1 || |
| | | zero[11] != IDENTIFY_CODE_C2 || |
| | | zero[12] != IDENTIFY_CODE_89) { |
| | | return null |
| | | } |
| | | |
| | | return clearCard |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | return null |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第0块数据 |
| | | */ |
| | | inner class Zero : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 设置备用位(0-7位) |
| | | for (i in 0..7) { |
| | | data[i] = 0x00 |
| | | } |
| | | |
| | | // 设置卡类型(8位) |
| | | data[8] = HexUtil.hexToByte(CARD_TYPE) |
| | | |
| | | // 设置识别码(9-12位) |
| | | data[9] = IDENTIFY_CODE_A0 |
| | | data[10] = IDENTIFY_CODE_B1 |
| | | data[11] = IDENTIFY_CODE_C2 |
| | | data[12] = IDENTIFY_CODE_89 |
| | | |
| | | // 设置备用位(13-14位) |
| | | data[13] = 0x00 |
| | | data[14] = 0x00 |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import java.io.Serializable |
| | | |
| | | /** |
| | | * 调试卡 |
| | | * 主要用于安装调试用: |
| | | * 1. 第一次刷卡时开启水泵或阀门 |
| | | * 2. 第二次刷卡关闭水泵或阀门 |
| | | * 3. 刷卡开启后1分钟内没有刷卡关闭,则自动关闭水泵或阀门 |
| | | */ |
| | | class DebugCard : BaseCard(), Serializable { |
| | | companion object { |
| | | const val CARD_TYPE = "B4" // 卡类型:调试卡 |
| | | const val IDENTIFY_CODE_A0 = 0xA0.toByte() // 识别码A0 |
| | | const val IDENTIFY_CODE_B1 = 0xB1.toByte() // 识别码B1 |
| | | const val IDENTIFY_CODE_C2 = 0xC2.toByte() // 识别码C2 |
| | | const val IDENTIFY_CODE_89 = 0x89.toByte() // 识别码89 |
| | | |
| | | const val AUTO_CLOSE_TIMEOUT = 60_000L // 自动关闭超时时间(1分钟) |
| | | } |
| | | |
| | | private var pumpOpenTime: Long = 0L // 水泵开启时间 |
| | | private var isPumpOpen: Boolean = false // 水泵状态 |
| | | |
| | | /** |
| | | * 写卡完成后校验是否写卡成功 |
| | | */ |
| | | fun equalsDebugCard(data: List<ByteArray>?): Boolean { |
| | | if (data == null || data.isEmpty()) { |
| | | return false |
| | | } |
| | | return data[0].contentEquals(getZeroBytes()) |
| | | } |
| | | |
| | | /** |
| | | * 通过byte转bean |
| | | */ |
| | | fun getBean(data: List<ByteArray>): DebugCard? { |
| | | try { |
| | | val debugCard = DebugCard() |
| | | // 解析第0块 |
| | | val zero = data[0] |
| | | |
| | | // 验证卡类型(8位) |
| | | if (HexUtil.byteToHex(zero[8]) != CARD_TYPE) { |
| | | return null |
| | | } |
| | | |
| | | // 验证识别码(9-12位) |
| | | if (zero[9] != IDENTIFY_CODE_A0 || |
| | | zero[10] != IDENTIFY_CODE_B1 || |
| | | zero[11] != IDENTIFY_CODE_C2 || |
| | | zero[12] != IDENTIFY_CODE_89) { |
| | | return null |
| | | } |
| | | |
| | | return debugCard |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | return null |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第0块数据 |
| | | */ |
| | | inner class Zero : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 设置备用位(0-7位) |
| | | for (i in 0..7) { |
| | | data[i] = 0x00 |
| | | } |
| | | |
| | | // 设置卡类型(8位) |
| | | data[8] = HexUtil.hexToByte(CARD_TYPE) |
| | | |
| | | // 设置识别码(9-12位) |
| | | data[9] = IDENTIFY_CODE_A0 |
| | | data[10] = IDENTIFY_CODE_B1 |
| | | data[11] = IDENTIFY_CODE_C2 |
| | | data[12] = IDENTIFY_CODE_89 |
| | | |
| | | // 设置备用位(13-14位) |
| | | data[13] = 0x00 |
| | | data[14] = 0x00 |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 切换水泵状态 |
| | | * @return 当前水泵状态 |
| | | */ |
| | | fun togglePumpState(): Boolean { |
| | | val currentTime = System.currentTimeMillis() |
| | | |
| | | if (!isPumpOpen) { |
| | | // 开启水泵 |
| | | isPumpOpen = true |
| | | pumpOpenTime = currentTime |
| | | } else { |
| | | // 关闭水泵 |
| | | isPumpOpen = false |
| | | pumpOpenTime = 0L |
| | | } |
| | | |
| | | return isPumpOpen |
| | | } |
| | | |
| | | /** |
| | | * 检查是否需要自动关闭水泵 |
| | | * @return true 如果需要自动关闭,false 否则 |
| | | */ |
| | | fun shouldAutoClose(): Boolean { |
| | | if (!isPumpOpen) return false |
| | | |
| | | val currentTime = System.currentTimeMillis() |
| | | return currentTime - pumpOpenTime >= AUTO_CLOSE_TIMEOUT |
| | | } |
| | | |
| | | /** |
| | | * 自动关闭水泵 |
| | | */ |
| | | fun autoClosePump() { |
| | | isPumpOpen = false |
| | | pumpOpenTime = 0L |
| | | } |
| | | |
| | | /** |
| | | * 获取水泵状态 |
| | | */ |
| | | fun isPumpOpen(): Boolean = isPumpOpen |
| | | |
| | | /** |
| | | * 获取水泵开启剩余时间(毫秒) |
| | | * @return 剩余时间,如果水泵关闭则返回0 |
| | | */ |
| | | fun getRemainingTime(): Long { |
| | | if (!isPumpOpen) return 0L |
| | | |
| | | val currentTime = System.currentTimeMillis() |
| | | val remainingTime = AUTO_CLOSE_TIMEOUT - (currentTime - pumpOpenTime) |
| | | return if (remainingTime > 0) remainingTime else 0L |
| | | } |
| | | |
| | | fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import java.io.Serializable |
| | | |
| | | /** |
| | | * 域名设置卡 |
| | | * 用于项目现场修改域名,通过刷卡方式修改域名 |
| | | */ |
| | | class DomainSettingCard : BaseCard(), Serializable { |
| | | companion object { |
| | | const val CARD_TYPE = "B6" // 卡类型:域名设置卡 |
| | | const val IDENTIFY_CODE_A0 = 0xA0.toByte() // 识别码A0 |
| | | const val IDENTIFY_CODE_B1 = 0xB1.toByte() // 识别码B1 |
| | | const val IDENTIFY_CODE_C2 = 0xC2.toByte() // 识别码C2 |
| | | const val IDENTIFY_CODE_89 = 0x89.toByte() // 识别码89 |
| | | |
| | | const val MAX_DOMAIN_ROUTE = 4 // 最大域名路数 |
| | | const val MAX_DOMAIN_LENGTH = 38 // 最大域名长度 |
| | | } |
| | | |
| | | var domainRouteNumber: Int = 1 // 域名序列号(1-4) |
| | | var domain: String = "" // 域名 |
| | | var domainLength: Int = 0 // 域名长度 |
| | | |
| | | /** |
| | | * 写卡完成后校验是否写卡成功 |
| | | */ |
| | | fun equalsDomainSettingCard(data: List<ByteArray>?): Boolean { |
| | | if (data == null || data.size < 3) { |
| | | return false |
| | | } |
| | | return data[0].contentEquals(getZeroBytes()) && |
| | | data[1].contentEquals(getOneBytes()) && |
| | | data[2].contentEquals(getTwoBytes()) |
| | | } |
| | | |
| | | /** |
| | | * 通过byte转bean |
| | | */ |
| | | fun getBean(data: List<ByteArray>): DomainSettingCard? { |
| | | try { |
| | | val domainSettingCard = DomainSettingCard() |
| | | |
| | | // 解析第0块 |
| | | val zero = data[0] |
| | | |
| | | // 验证卡类型(8位) |
| | | if (HexUtil.byteToHex(zero[8]) != CARD_TYPE) { |
| | | return null |
| | | } |
| | | |
| | | // 验证识别码(9-12位) |
| | | if (zero[9] != IDENTIFY_CODE_A0 || |
| | | zero[10] != IDENTIFY_CODE_B1 || |
| | | zero[11] != IDENTIFY_CODE_C2 || |
| | | zero[12] != IDENTIFY_CODE_89) { |
| | | return null |
| | | } |
| | | |
| | | // 解析域名路数(13位) |
| | | val routeNumber = zero[13].toInt() |
| | | if (routeNumber !in 1..MAX_DOMAIN_ROUTE) { |
| | | return null |
| | | } |
| | | domainSettingCard.domainRouteNumber = routeNumber |
| | | |
| | | // 解析域名长度(14位) |
| | | domainSettingCard.domainLength = zero[14].toInt() |
| | | |
| | | // 解析域名前8个字符(0-7位) |
| | | val domainBuilder = StringBuilder() |
| | | for (i in 0..7) { |
| | | if (zero[i] != 0.toByte()) { |
| | | domainBuilder.append(zero[i].toInt().toChar()) |
| | | } |
| | | } |
| | | |
| | | // 解析第1块 - 域名中间15个字符 |
| | | val one = data[1] |
| | | for (i in 0..14) { |
| | | if (one[i] != 0.toByte()) { |
| | | domainBuilder.append(one[i].toInt().toChar()) |
| | | } |
| | | } |
| | | |
| | | // 解析第2块 - 域名剩余15个字符 |
| | | val two = data[2] |
| | | for (i in 0..14) { |
| | | if (two[i] != 0.toByte()) { |
| | | domainBuilder.append(two[i].toInt().toChar()) |
| | | } |
| | | } |
| | | |
| | | domainSettingCard.domain = domainBuilder.toString() |
| | | |
| | | return domainSettingCard |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | return null |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第0块数据 |
| | | */ |
| | | inner class Zero : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 写入域名前8个字符(0-7位) |
| | | domain.take(8).forEachIndexed { index, char -> |
| | | data[index] = char.code.toByte() |
| | | } |
| | | // 补0 |
| | | for (i in domain.length.coerceAtMost(8) until 8) { |
| | | data[i] = 0 |
| | | } |
| | | |
| | | // 设置卡类型(8位) |
| | | data[8] = HexUtil.hexToByte(CARD_TYPE) |
| | | |
| | | // 设置识别码(9-12位) |
| | | data[9] = IDENTIFY_CODE_A0 |
| | | data[10] = IDENTIFY_CODE_B1 |
| | | data[11] = IDENTIFY_CODE_C2 |
| | | data[12] = IDENTIFY_CODE_89 |
| | | |
| | | // 设置域名路数(13位) |
| | | data[13] = domainRouteNumber.toByte() |
| | | |
| | | // 设置域名长度(14位) |
| | | data[14] = domain.length.toByte() |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第1块数据 - 域名中间部分 |
| | | */ |
| | | inner class One : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 写入域名中间15个字符(0-14位) |
| | | domain.drop(8).take(15).forEachIndexed { index, char -> |
| | | data[index] = char.code.toByte() |
| | | } |
| | | // 补0 |
| | | for (i in (domain.length - 8).coerceAtMost(15) until 15) { |
| | | data[i] = 0 |
| | | } |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第2块数据 - 域名结尾部分 |
| | | */ |
| | | inner class Two : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 写入域名最后15个字符(0-14位) |
| | | domain.drop(23).take(15).forEachIndexed { index, char -> |
| | | data[index] = char.code.toByte() |
| | | } |
| | | // 补0 |
| | | for (i in (domain.length - 23).coerceAtMost(15) until 15) { |
| | | data[i] = 0 |
| | | } |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 验证域名格式是否正确 |
| | | */ |
| | | fun isValidDomain(): Boolean { |
| | | return domain.isNotEmpty() && |
| | | domain.length <= MAX_DOMAIN_LENGTH && |
| | | domain.matches(Regex("^[a-zA-Z0-9.-]+$")) |
| | | } |
| | | |
| | | /** |
| | | * 验证域名路数是否正确 |
| | | */ |
| | | fun isValidRouteNumber(): Boolean { |
| | | return domainRouteNumber in 1..MAX_DOMAIN_ROUTE |
| | | } |
| | | |
| | | fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | fun getOneBytes(): ByteArray = One().toBytes() |
| | | fun getTwoBytes(): ByteArray = Two().toBytes() |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.tools.BcdUtil |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import java.io.Serializable |
| | | |
| | | /** |
| | | * 取数卡 |
| | | * 用于在用户卡丢失后,读取当前用户或挂起用户的卡内信息,作为补卡依据 |
| | | */ |
| | | class FetchDataCard : BaseCard(), Serializable { |
| | | companion object { |
| | | const val CARD_TYPE_NEED_FETCH = "B1" // 需要刷卡取数 |
| | | const val CARD_TYPE_FETCH_SUCCESS = "B2" // 刷卡取数返写成功 |
| | | const val IDENTIFY_CODE_A0 = 0xA0.toByte() // 识别码A0 |
| | | const val IDENTIFY_CODE_B1 = 0xB1.toByte() // 识别码B1 |
| | | const val IDENTIFY_CODE_C2 = 0xC2.toByte() // 识别码C2 |
| | | const val IDENTIFY_CODE_89 = 0x89.toByte() // 识别码89 |
| | | } |
| | | |
| | | var areaNumber: Int = 0 // 国家行政区域号(12位BCD,精确到村) |
| | | var userNumber: Int = 0 // 用户编号(HEX) |
| | | var cardType: String = CARD_TYPE_NEED_FETCH // 卡类型:B1需要刷卡取数,B2刷卡取数返写成功 |
| | | var projectCode: Int = 0 // 项目编码(HEX 1-255) |
| | | |
| | | /** |
| | | * 写卡完成后校验是否写卡成功 |
| | | */ |
| | | fun equalsFetchDataCard(data: List<ByteArray>?): Boolean { |
| | | if (data == null || data.isEmpty()) { |
| | | return false |
| | | } |
| | | return data[0].contentEquals(getZeroBytes()) |
| | | } |
| | | |
| | | /** |
| | | * 通过byte转bean |
| | | */ |
| | | fun getBean(data: List<ByteArray>): FetchDataCard? { |
| | | try { |
| | | val fetchDataCard = FetchDataCard() |
| | | // 解析第0块 |
| | | val zero = data[0] |
| | | |
| | | // 解析国家行政区域号(0-5位) |
| | | val areaCodeBytes = zero.copyOfRange(0, 6) |
| | | fetchDataCard.areaNumber = BcdUtil.bcdToStr(areaCodeBytes).toInt() |
| | | |
| | | // 解析用户编号(6-7位) |
| | | val userNumberBytes = zero.copyOfRange(6, 8) |
| | | fetchDataCard.userNumber = HexUtil.get16To10LowHightByBytes(userNumberBytes) |
| | | |
| | | // 解析卡类型(8位) |
| | | val cardType = HexUtil.byteToHex(zero[8]) |
| | | if (cardType != CARD_TYPE_NEED_FETCH && cardType != CARD_TYPE_FETCH_SUCCESS) { |
| | | return null |
| | | } |
| | | fetchDataCard.cardType = cardType |
| | | |
| | | // 验证识别码(9-12位) |
| | | if (zero[9] != IDENTIFY_CODE_A0 || |
| | | zero[10] != IDENTIFY_CODE_B1 || |
| | | zero[11] != IDENTIFY_CODE_C2 || |
| | | zero[12] != IDENTIFY_CODE_89) { |
| | | return null |
| | | } |
| | | |
| | | // 解析项目编码(13位) |
| | | fetchDataCard.projectCode = HexUtil.get16To10LowHightByBytes(byteArrayOf(zero[13])) |
| | | |
| | | return fetchDataCard |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | return null |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第0块数据 |
| | | */ |
| | | inner class Zero : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 设置国家行政区域号(BCD格式,6字节,0-5位) |
| | | val areaCodeBytes = BcdUtil.strToBcd(String.format("%012d", areaNumber)) |
| | | System.arraycopy(areaCodeBytes, 0, data, 0, 6) |
| | | |
| | | // 设置用户编号(6-7位) |
| | | val userNumberBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(userNumber)) |
| | | System.arraycopy(userNumberBytes, 0, data, 6, 2) |
| | | |
| | | // 设置卡类型(8位) |
| | | data[8] = HexUtil.hexToByte(cardType) |
| | | |
| | | // 设置识别码(9-12位) |
| | | data[9] = IDENTIFY_CODE_A0 |
| | | data[10] = IDENTIFY_CODE_B1 |
| | | data[11] = IDENTIFY_CODE_C2 |
| | | data[12] = IDENTIFY_CODE_89 |
| | | |
| | | // 设置项目编码(13位) |
| | | data[13] = projectCode.toByte() |
| | | |
| | | // 设置备用位(14位) |
| | | data[14] = 0x00 |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 将卡片状态更新为取数成功 |
| | | */ |
| | | fun markAsFetchSuccess() { |
| | | cardType = CARD_TYPE_FETCH_SUCCESS |
| | | } |
| | | |
| | | /** |
| | | * 检查是否已经取数成功 |
| | | */ |
| | | fun isFetchSuccess(): Boolean { |
| | | return cardType == CARD_TYPE_FETCH_SUCCESS |
| | | } |
| | | |
| | | fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import java.io.Serializable |
| | | |
| | | /** |
| | | * GPS定位卡 |
| | | * 主要用于内部生产调试用 |
| | | */ |
| | | class GpsCard : BaseCard(), Serializable { |
| | | companion object { |
| | | const val CARD_TYPE = "B7" // 卡类型:GPS定位卡 |
| | | const val IDENTIFY_CODE_A0 = 0xA0.toByte() // 识别码A0 |
| | | const val IDENTIFY_CODE_B1 = 0xB1.toByte() // 识别码B1 |
| | | const val IDENTIFY_CODE_C2 = 0xC2.toByte() // 识别码C2 |
| | | const val IDENTIFY_CODE_89 = 0x89.toByte() // 识别码89 |
| | | } |
| | | |
| | | /** |
| | | * 写卡完成后校验是否写卡成功 |
| | | */ |
| | | fun equalsGpsCard(data: List<ByteArray>?): Boolean { |
| | | if (data == null || data.isEmpty()) { |
| | | return false |
| | | } |
| | | return data[0].contentEquals(getZeroBytes()) |
| | | } |
| | | |
| | | /** |
| | | * 通过byte转bean |
| | | */ |
| | | fun getBean(data: List<ByteArray>): GpsCard? { |
| | | try { |
| | | val gpsCard = GpsCard() |
| | | // 解析第0块 |
| | | val zero = data[0] |
| | | |
| | | // 验证卡类型(8位) |
| | | if (HexUtil.byteToHex(zero[8]) != CARD_TYPE) { |
| | | return null |
| | | } |
| | | |
| | | // 验证识别码(9-12位) |
| | | if (zero[9] != IDENTIFY_CODE_A0 || |
| | | zero[10] != IDENTIFY_CODE_B1 || |
| | | zero[11] != IDENTIFY_CODE_C2 || |
| | | zero[12] != IDENTIFY_CODE_89) { |
| | | return null |
| | | } |
| | | |
| | | return gpsCard |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | return null |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第0块数据 |
| | | */ |
| | | inner class Zero : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 设置备用位(0-7位) |
| | | for (i in 0..7) { |
| | | data[i] = 0x00 |
| | | } |
| | | |
| | | // 设置卡类型(8位) |
| | | data[8] = HexUtil.hexToByte(CARD_TYPE) |
| | | |
| | | // 设置识别码(9-12位) |
| | | data[9] = IDENTIFY_CODE_A0 |
| | | data[10] = IDENTIFY_CODE_B1 |
| | | data[11] = IDENTIFY_CODE_C2 |
| | | data[12] = IDENTIFY_CODE_89 |
| | | |
| | | // 设置备用位(13-14位) |
| | | data[13] = 0x00 |
| | | data[14] = 0x00 |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import java.io.Serializable |
| | | |
| | | /** |
| | | * IP地址设置卡 |
| | | * 用于项目现场修改IP地址,通过刷卡方式修改IP地址 |
| | | */ |
| | | class IpSettingCard : BaseCard(), Serializable { |
| | | companion object { |
| | | const val CARD_TYPE = "B5" // 卡类型:IP设置卡 |
| | | const val IDENTIFY_CODE_A0 = 0xA0.toByte() // 识别码A0 |
| | | const val IDENTIFY_CODE_B1 = 0xB1.toByte() // 识别码B1 |
| | | const val IDENTIFY_CODE_C2 = 0xC2.toByte() // 识别码C2 |
| | | const val IDENTIFY_CODE_89 = 0x89.toByte() // 识别码89 |
| | | |
| | | const val MAX_IP_ROUTE = 4 // 最大IP路数 |
| | | } |
| | | |
| | | var ipRouteNumber: Int = 1 // IP地址序列号(1-4) |
| | | var ipAddress: String = "" // IP地址 |
| | | var port: Int = 0 // 端口号 |
| | | |
| | | /** |
| | | * 写卡完成后校验是否写卡成功 |
| | | */ |
| | | fun equalsIpSettingCard(data: List<ByteArray>?): Boolean { |
| | | if (data == null || data.size < 3) { |
| | | return false |
| | | } |
| | | return data[0].contentEquals(getZeroBytes()) && |
| | | data[1].contentEquals(getOneBytes()) && |
| | | data[2].contentEquals(getTwoBytes()) |
| | | } |
| | | |
| | | /** |
| | | * 通过byte转bean |
| | | */ |
| | | fun getBean(data: List<ByteArray>): IpSettingCard? { |
| | | try { |
| | | val ipSettingCard = IpSettingCard() |
| | | |
| | | // 解析第0块 |
| | | val zero = data[0] |
| | | |
| | | // 验证卡类型(8位) |
| | | if (HexUtil.byteToHex(zero[8]) != CARD_TYPE) { |
| | | return null |
| | | } |
| | | |
| | | // 验证识别码(9-12位) |
| | | if (zero[9] != IDENTIFY_CODE_A0 || |
| | | zero[10] != IDENTIFY_CODE_B1 || |
| | | zero[11] != IDENTIFY_CODE_C2 || |
| | | zero[12] != IDENTIFY_CODE_89) { |
| | | return null |
| | | } |
| | | |
| | | // 解析IP路数(13位) |
| | | val routeNumber = zero[13].toInt() |
| | | if (routeNumber !in 1..MAX_IP_ROUTE) { |
| | | return null |
| | | } |
| | | ipSettingCard.ipRouteNumber = routeNumber |
| | | |
| | | // 解析第1块 - IP地址 |
| | | val one = data[1] |
| | | val ipBuilder = StringBuilder() |
| | | for (i in 0..14) { |
| | | ipBuilder.append(one[i].toInt().toChar()) |
| | | } |
| | | ipSettingCard.ipAddress = ipBuilder.toString() |
| | | |
| | | // 解析第2块 - 端口号 |
| | | val two = data[2] |
| | | val portBuilder = StringBuilder() |
| | | for (i in 0..4) { |
| | | portBuilder.append(two[i].toInt().toChar()) |
| | | } |
| | | ipSettingCard.port = portBuilder.toString().toInt() |
| | | |
| | | return ipSettingCard |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | return null |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第0块数据 |
| | | */ |
| | | inner class Zero : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 设置备用位(0-7位) |
| | | for (i in 0..7) { |
| | | data[i] = 0x00 |
| | | } |
| | | |
| | | // 设置卡类型(8位) |
| | | data[8] = HexUtil.hexToByte(CARD_TYPE) |
| | | |
| | | // 设置识别码(9-12位) |
| | | data[9] = IDENTIFY_CODE_A0 |
| | | data[10] = IDENTIFY_CODE_B1 |
| | | data[11] = IDENTIFY_CODE_C2 |
| | | data[12] = IDENTIFY_CODE_89 |
| | | |
| | | // 设置IP路数(13位) |
| | | data[13] = ipRouteNumber.toByte() |
| | | |
| | | // 设置备用位(14位) |
| | | data[14] = 0x00 |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第1块数据 - IP地址 |
| | | */ |
| | | inner class One : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 格式化IP地址 |
| | | val formattedIp = formatIpAddress(ipAddress) |
| | | |
| | | // 写入IP地址(0-14位) |
| | | formattedIp.forEachIndexed { index, char -> |
| | | data[index] = char.code.toByte() |
| | | } |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第2块数据 - 端口号 |
| | | */ |
| | | inner class Two : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 格式化端口号 |
| | | val formattedPort = String.format("%05d", port) |
| | | |
| | | // 写入端口号(0-4位) |
| | | formattedPort.forEachIndexed { index, char -> |
| | | data[index] = char.code.toByte() |
| | | } |
| | | |
| | | // 设置备用位(5-14位) |
| | | for (i in 5..14) { |
| | | data[i] = 0x00 |
| | | } |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 格式化IP地址 |
| | | * 将IP地址格式化为标准格式:每组3位,不足补0 |
| | | */ |
| | | private fun formatIpAddress(ip: String): String { |
| | | val parts = ip.split(".") |
| | | if (parts.size != 4) throw IllegalArgumentException("Invalid IP address format") |
| | | |
| | | return parts.joinToString(".") { part -> |
| | | String.format("%03d", part.toInt()) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 验证IP地址格式是否正确 |
| | | */ |
| | | fun isValidIpAddress(): Boolean { |
| | | return try { |
| | | val parts = ipAddress.split(".") |
| | | if (parts.size != 4) return false |
| | | |
| | | parts.all { part -> |
| | | val num = part.toInt() |
| | | num in 0..255 |
| | | } |
| | | } catch (e: Exception) { |
| | | false |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 验证端口号是否正确 |
| | | */ |
| | | fun isValidPort(): Boolean { |
| | | return port in 1..65535 |
| | | } |
| | | |
| | | fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | fun getOneBytes(): ByteArray = One().toBytes() |
| | | fun getTwoBytes(): ByteArray = Two().toBytes() |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.bean.BaseUserCardCard |
| | | import com.dayu.baselibrary.tools.BcdUtil |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import java.io.Serializable |
| | | import java.util.* |
| | | |
| | | /** |
| | | * 通用项目用户卡结构 |
| | | */ |
| | | class UserCard : BaseCard(), Serializable { |
| | | var cardType: String = "A1" // 卡类型:A1终端写卡 A8刷卡开泵后值 A2叠加充值 |
| | | var areaNumber: Int = 0 // 国家行政区域号(12位BCD,精确到村) |
| | | var userCode: String = "" // 用户编号BCD |
| | | var userCodeNumber: Int = 0 // 用户卡编号(HEX) |
| | | var phoneNumber: String = "" // 手机号(BCD) |
| | | var projectCode: Int = 0 // 项目编码(HEX 1-255) |
| | | var balance: Int = 0 // 剩余金额(2位小数点,单位元) |
| | | var surplusWater: Int = 0 // 剩余水量(2位小数点,单位立方米) |
| | | var waterPrice: Float = 0f // 水量单价(最大655.35,2位小数点。单位:m3/元) |
| | | var electricPrice: Float = 0f // 电量单价(最大655.35,2位小数点。单位: 元/度) |
| | | var rechargeDate: Calendar? = null // 充值时间 |
| | | |
| | | /** |
| | | * 写卡完成后校验是否写卡成功 |
| | | */ |
| | | fun equalsUserCard(data: List<ByteArray>?): Boolean { |
| | | if (data == null || data.size < 3) { |
| | | return false |
| | | } |
| | | |
| | | val expectedBytes = arrayOf(getZeroBytes(), getOneBytes(), getTwoBytes()) |
| | | return data.zip(expectedBytes.toList()).all { (actual, expected) -> |
| | | actual.contentEquals(expected) |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 返回完整的用户编号 |
| | | */ |
| | | fun getMyUserCode(): String { |
| | | return userCode + String.format("%04d", userCodeNumber) |
| | | } |
| | | |
| | | /** |
| | | * 通过byte转bean |
| | | */ |
| | | fun getBean(data: List<ByteArray>): UserCard? { |
| | | try { |
| | | val userCard = UserCard() |
| | | // 解析第0块 |
| | | val zero = data[0] |
| | | userCard.apply { |
| | | // 解析国家行政区域号(0-5位) |
| | | val areaCodeBytes = zero.copyOfRange(0, 6) |
| | | areaNumber = BcdUtil.bcdToStr(areaCodeBytes).toInt() |
| | | |
| | | // 解析用户卡编号(6-7位) |
| | | val userCodeNumberBytes = zero.copyOfRange(6, 8) |
| | | userCodeNumber = HexUtil.get16To10LowHightByBytes(userCodeNumberBytes) |
| | | |
| | | // 解析卡类型(8位) |
| | | cardType = HexUtil.byteToHex(zero[8]) |
| | | |
| | | // 解析手机号(9-14位) |
| | | phoneNumber = BcdUtil.bcdToStr(zero.copyOfRange(9, 15)) |
| | | } |
| | | |
| | | // 解析第1块 |
| | | val one = data[1] |
| | | userCard.apply { |
| | | projectCode = HexUtil.get16To10LowHightByBytes(byteArrayOf(one[0])) |
| | | balance = HexUtil.get16To10LowHightByBytes(one.copyOfRange(1, 5)) |
| | | surplusWater = HexUtil.get16To10LowHightByBytes(one.copyOfRange(5, 9)) |
| | | electricPrice = HexUtil.hexToFloatLowHigh(one.copyOfRange(9, 11)) |
| | | |
| | | // 解析充值时间 |
| | | val year = HexUtil.getBcdToInt(one[11]) |
| | | val month = HexUtil.getBcdToInt(one[12]) |
| | | val day = HexUtil.getBcdToInt(one[13]) |
| | | val hour = HexUtil.getBcdToInt(one[14]) |
| | | Calendar.getInstance().apply { |
| | | set(2000 + year, month - 1, day, hour, 0, 0) |
| | | rechargeDate = this |
| | | } |
| | | } |
| | | |
| | | // 解析第2块 |
| | | val two = data[2] |
| | | userCard.apply { |
| | | waterPrice = HexUtil.hexToFloatLowHigh(two.copyOfRange(9, 11)) |
| | | } |
| | | |
| | | return userCard |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | return null |
| | | } |
| | | } |
| | | |
| | | // 内部类用于生成各个数据块 |
| | | inner class Zero : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 设置国家行政区域号(BCD格式,6字节,0-5位) |
| | | val areaCodeBytes = BcdUtil.strToBcd(String.format("%012d", areaNumber)) |
| | | System.arraycopy(areaCodeBytes, 0, data, 0, 6) |
| | | |
| | | // 设置用户卡编号(HEX格式,2字节,6-7位) |
| | | val userCodeBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(userCodeNumber)) |
| | | System.arraycopy(userCodeBytes, 0, data, 6, 2) |
| | | |
| | | // 设置卡类型(8位) |
| | | data[8] = HexUtil.hexToByte(cardType) |
| | | |
| | | // 设置手机号(BCD格式,6字节,9-14位) |
| | | val phoneBytes = BcdUtil.strToBcd(phoneNumber.padStart(12, '0')) |
| | | System.arraycopy(phoneBytes, 0, data, 9, 6) |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | inner class One : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | data[0] = projectCode.toByte() |
| | | |
| | | // 设置余额 |
| | | val balanceBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(balance)) |
| | | System.arraycopy(balanceBytes, 0, data, 1, 4) |
| | | |
| | | // 设置剩余水量 |
| | | val waterBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(surplusWater)) |
| | | System.arraycopy(waterBytes, 0, data, 5, 4) |
| | | |
| | | // 设置电价 |
| | | val priceBytes = HexUtil.hexToByteArray(HexUtil.floatToHexLowHigh(electricPrice)) |
| | | System.arraycopy(priceBytes, 0, data, 9, 2) |
| | | |
| | | // 设置充值时间 |
| | | rechargeDate?.let { |
| | | data[11] = HexUtil.getIntToBCD(it.get(Calendar.YEAR) % 100)[0] |
| | | data[12] = HexUtil.getIntToBCD(it.get(Calendar.MONTH) + 1)[0] |
| | | data[13] = HexUtil.getIntToBCD(it.get(Calendar.DAY_OF_MONTH))[0] |
| | | data[14] = HexUtil.getIntToBCD(it.get(Calendar.HOUR_OF_DAY))[0] |
| | | } |
| | | |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | inner class Two : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 备份余额和水量数据 |
| | | val balanceBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(balance)) |
| | | System.arraycopy(balanceBytes, 0, data, 1, 4) |
| | | |
| | | val waterBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(surplusWater)) |
| | | System.arraycopy(waterBytes, 0, data, 5, 4) |
| | | |
| | | // 设置水价 |
| | | val priceBytes = HexUtil.hexToByteArray(HexUtil.floatToHexLowHigh(waterPrice)) |
| | | System.arraycopy(priceBytes, 0, data, 9, 2) |
| | | |
| | | // 设置充值时间 |
| | | rechargeDate?.let { |
| | | data[11] = HexUtil.getIntToBCD(it.get(Calendar.YEAR) % 100)[0] |
| | | data[12] = HexUtil.getIntToBCD(it.get(Calendar.MONTH) + 1)[0] |
| | | data[13] = HexUtil.getIntToBCD(it.get(Calendar.DAY_OF_MONTH))[0] |
| | | data[14] = HexUtil.getIntToBCD(it.get(Calendar.HOUR_OF_DAY))[0] |
| | | } |
| | | |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | fun getOneBytes(): ByteArray = One().toBytes() |
| | | fun getTwoBytes(): ByteArray = Two().toBytes() |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.card |
| | | |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import java.io.Serializable |
| | | |
| | | /** |
| | | * 配置开关阀时间卡 |
| | | * 主要用于内部生产调试用,设置开关阀时间 |
| | | */ |
| | | class ValveTimeCard : BaseCard(), Serializable { |
| | | companion object { |
| | | const val CARD_TYPE = "B8" // 卡类型:配置开关阀时间卡 |
| | | const val IDENTIFY_CODE_A0 = 0xA0.toByte() // 识别码A0 |
| | | const val IDENTIFY_CODE_B1 = 0xB1.toByte() // 识别码B1 |
| | | const val IDENTIFY_CODE_C2 = 0xC2.toByte() // 识别码C2 |
| | | const val IDENTIFY_CODE_89 = 0x89.toByte() // 识别码89 |
| | | } |
| | | |
| | | var valveTime: Int = 0 // 开关阀时间(秒) |
| | | |
| | | /** |
| | | * 写卡完成后校验是否写卡成功 |
| | | */ |
| | | fun equalsValveTimeCard(data: List<ByteArray>?): Boolean { |
| | | if (data == null || data.isEmpty()) { |
| | | return false |
| | | } |
| | | return data[0].contentEquals(getZeroBytes()) |
| | | } |
| | | |
| | | /** |
| | | * 通过byte转bean |
| | | */ |
| | | fun getBean(data: List<ByteArray>): ValveTimeCard? { |
| | | try { |
| | | val valveTimeCard = ValveTimeCard() |
| | | // 解析第0块 |
| | | val zero = data[0] |
| | | |
| | | // 验证卡类型(8位) |
| | | if (HexUtil.byteToHex(zero[8]) != CARD_TYPE) { |
| | | return null |
| | | } |
| | | |
| | | // 验证识别码(9-12位) |
| | | if (zero[9] != IDENTIFY_CODE_A0 || |
| | | zero[10] != IDENTIFY_CODE_B1 || |
| | | zero[11] != IDENTIFY_CODE_C2 || |
| | | zero[12] != IDENTIFY_CODE_89) { |
| | | return null |
| | | } |
| | | |
| | | // 解析开关阀时间(13-14位) |
| | | val timeBytes = byteArrayOf(zero[13], zero[14]) |
| | | valveTimeCard.valveTime = HexUtil.get16To10LowHightByBytes(timeBytes) |
| | | |
| | | return valveTimeCard |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | return null |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 生成第0块数据 |
| | | */ |
| | | inner class Zero : BaseCard() { |
| | | fun toBytes(): ByteArray { |
| | | val data = ByteArray(16) |
| | | try { |
| | | // 设置备用位(0-7位) |
| | | for (i in 0..7) { |
| | | data[i] = 0x00 |
| | | } |
| | | |
| | | // 设置卡类型(8位) |
| | | data[8] = HexUtil.hexToByte(CARD_TYPE) |
| | | |
| | | // 设置识别码(9-12位) |
| | | data[9] = IDENTIFY_CODE_A0 |
| | | data[10] = IDENTIFY_CODE_B1 |
| | | data[11] = IDENTIFY_CODE_C2 |
| | | data[12] = IDENTIFY_CODE_89 |
| | | |
| | | // 设置开关阀时间(13-14位),低位在前高位在后 |
| | | val timeBytes = HexUtil.hexToByteArray(HexUtil.get10To16LowHigh(valveTime)) |
| | | if (timeBytes.size >= 2) { |
| | | data[13] = timeBytes[0] // 低位 |
| | | data[14] = timeBytes[1] // 高位 |
| | | } |
| | | |
| | | // 设置校验和(15位) |
| | | data[15] = getByteSum(data) |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | return data |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 验证开关阀时间是否有效 |
| | | * @return true 如果时间有效,false 否则 |
| | | */ |
| | | fun isValidValveTime(): Boolean { |
| | | return valveTime > 0 && valveTime <= 65535 // 两个字节最大值 |
| | | } |
| | | |
| | | /** |
| | | * 获取开关阀时间的十六进制字符串表示 |
| | | * @return 十六进制字符串,如"0087"表示135秒 |
| | | */ |
| | | fun getValveTimeHex(): String { |
| | | return String.format("%04X", valveTime) |
| | | } |
| | | |
| | | fun getZeroBytes(): ByteArray = Zero().toBytes() |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.db |
| | | |
| | | import androidx.room.Entity |
| | | import androidx.room.PrimaryKey |
| | | |
| | | @Entity |
| | | class CardData { |
| | | @PrimaryKey(autoGenerate = true) |
| | | var id: Long = 0 |
| | | |
| | | /** |
| | | * 卡片类型 |
| | | */ |
| | | var cardType: String? = null |
| | | |
| | | /** |
| | | * 卡标识 |
| | | */ |
| | | var cardIdentifying: String? = null |
| | | |
| | | } |
New file |
| | |
| | | package com.dayu.general.bean.db; |
| | | |
| | | import androidx.room.Entity; |
| | | import androidx.room.PrimaryKey; |
| | | |
| | | /** |
| | | * author: zuo |
| | | * Date: 2024-03-01 |
| | | * Time: 17:56 |
| | | * 备注:密码卡密码 |
| | | */ |
| | | @Entity |
| | | public class PassWordCardBean { |
| | | |
| | | @PrimaryKey(autoGenerate = true) |
| | | public long id; |
| | | private String passWord = ""; |
| | | |
| | | public long getId() { |
| | | return id; |
| | | } |
| | | |
| | | public void setId(long id) { |
| | | this.id = id; |
| | | } |
| | | |
| | | public String getPassWord() { |
| | | return passWord; |
| | | } |
| | | |
| | | public void setPassWord(String passWord) { |
| | | this.passWord = passWord; |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | package com.dayu.general.dao |
| | | |
| | | import androidx.room.Database |
| | | import androidx.room.RoomDatabase |
| | | import com.dayu.general.bean.db.CardData |
| | | import com.dayu.general.bean.db.PassWordCardBean |
| | | |
| | | @Database(entities = [PassWordCardBean::class, CardData::class], version = 1, exportSchema = false) |
| | | abstract class AppDataBase : RoomDatabase() { |
| | | abstract fun cardDataDao(): CardDataDao |
| | | } |
| | |
| | | return AsynchBaseDao |
| | | } |
| | | |
| | | companion object { |
| | | fun getAsynchInstance(baseActivity: BaseActivity): AppDataBase? { |
| | | return getAsynchInstance(baseActivity) |
| | | } |
| | | } |
| | | // companion object { |
| | | // fun getAsynchInstance(baseActivity: BaseActivity): AppDataBase? { |
| | | // return getAsynchInstance(baseActivity) |
| | | // } |
| | | // } |
| | | } |
New file |
| | |
| | | package com.dayu.general.dao |
| | | |
| | | import androidx.room.Dao |
| | | import androidx.room.Insert |
| | | import androidx.room.OnConflictStrategy |
| | | import androidx.room.Query |
| | | import androidx.room.Update |
| | | import com.dayu.general.bean.db.CardData |
| | | |
| | | @Dao |
| | | interface CardDataDao { |
| | | @Insert(onConflict = OnConflictStrategy.REPLACE) |
| | | fun insert(cardData: CardData) |
| | | |
| | | @Update |
| | | fun update(cardData: CardData) |
| | | |
| | | @Query("select * from CardData where cardType=:cardType limit 1") |
| | | fun findFirst(cardType: String?): CardData |
| | | } |
New file |
| | |
| | | package com.dayu.general.tool |
| | | |
| | | import android.content.Context |
| | | import android.util.Base64 |
| | | import com.dayu.baselibrary.tools.HexUtil |
| | | import com.dayu.baselibrary.tools.nfc.BaseNFCHelper |
| | | import java.nio.charset.StandardCharsets |
| | | import javax.crypto.Cipher |
| | | import javax.crypto.spec.SecretKeySpec |
| | | |
| | | class GeBaseHelper(private val context: Context) : BaseNFCHelper() { |
| | | |
| | | |
| | | companion object { |
| | | init { |
| | | System.loadLibrary("general-native-lib") |
| | | } |
| | | } |
| | | |
| | | private external fun getM1SectorKeySecure(context: Context, sectorIndex: Int): String |
| | | |
| | | init { |
| | | try { |
| | | // 获取所有扇区密钥 |
| | | val allKeys: String = getM1SectorKeySecure(context, 0) |
| | | val keys: Array<String> = |
| | | allKeys.split(",").dropLastWhile { it.isEmpty() }.toTypedArray() |
| | | for (i in keys.indices) { |
| | | val key = keys[i] |
| | | listA_PS.add(HexUtil.hexToByteArray(key)) |
| | | } |
| | | defauleKey = HexUtil.hexToByteArray("FFFFFFFFFFFF") |
| | | |
| | | |
| | | } catch (e: Exception) { |
| | | e.printStackTrace() |
| | | } |
| | | } |
| | | |
| | | fun getKeyList(): List<ByteArray> { |
| | | return listKeyA |
| | | } |
| | | } |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <shape xmlns:android="http://schemas.android.com/apk/res/android"> |
| | | <solid android:color="@color/bottom_color" /> <!-- 按钮的背景色 --> |
| | | <corners android:radius="90dp" /> <!-- 让按钮的四个角变成圆形 --> |
| | | <corners android:radius="100dp" /> <!-- 让按钮的四个角变成圆形 --> |
| | | <size android:width="100dp" android:height="100dp" /> <!-- 设置按钮的宽高 --> |
| | | </shape> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> |
| | | <!-- 阴影层 --> |
| | | <item> |
| | | <shape> |
| | | <padding |
| | | android:bottom="0dp" |
| | | android:left="0dp" |
| | | android:right="0dp" |
| | | android:top="2dp" /> |
| | | <solid android:color="#10000000" /> |
| | | <corners |
| | | android:topLeftRadius="18dp" |
| | | android:topRightRadius="18dp" /> |
| | | </shape> |
| | | </item> |
| | | <item> |
| | | <shape> |
| | | <padding |
| | | android:bottom="0dp" |
| | | android:left="0dp" |
| | | android:right="0dp" |
| | | android:top="2dp" /> |
| | | <solid android:color="#15000000" /> |
| | | <corners |
| | | android:topLeftRadius="18dp" |
| | | android:topRightRadius="18dp" /> |
| | | </shape> |
| | | </item> |
| | | <!-- 主背景 --> |
| | | <item> |
| | | <shape> |
| | | <solid android:color="#FFFFFF" /> |
| | | <corners |
| | | android:topLeftRadius="18dp" |
| | | android:topRightRadius="18dp" /> |
| | | </shape> |
| | | </item> |
| | | </layer-list> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> |
| | | <!-- 阴影层 --> |
| | | <item> |
| | | <shape android:shape="oval"> |
| | | <solid android:color="#30000000" /> |
| | | <padding android:bottom="1dp" android:left="1dp" android:right="1dp" android:top="1dp" /> |
| | | </shape> |
| | | </item> |
| | | <!-- 主背景 --> |
| | | <item> |
| | | <shape android:shape="oval"> |
| | | <gradient |
| | | android:angle="135" |
| | | android:endColor="#1E88E5" |
| | | android:centerColor="#4285F4" |
| | | android:startColor="#5C9CE6" |
| | | android:type="linear" /> |
| | | </shape> |
| | | </item> |
| | | </layer-list> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <selector xmlns:android="http://schemas.android.com/apk/res/android"> |
| | | <item android:color="#4285F4" android:state_selected="true" /> |
| | | <item android:color="#555555" /> |
| | | </selector> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <ripple xmlns:android="http://schemas.android.com/apk/res/android" |
| | | android:color="#20000000"> |
| | | <item android:id="@android:id/mask"> |
| | | <shape android:shape="rectangle"> |
| | | <solid android:color="#000000" /> |
| | | <corners android:radius="4dp" /> |
| | | </shape> |
| | | </item> |
| | | </ripple> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <shape xmlns:android="http://schemas.android.com/apk/res/android" |
| | | android:shape="rectangle"> |
| | | <corners android:radius="16dp" /> |
| | | <solid android:color="@color/bottom_color" /> |
| | | </shape> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <shape xmlns:android="http://schemas.android.com/apk/res/android"> |
| | | <gradient |
| | | android:angle="90" |
| | | android:endColor="#00000000" |
| | | android:startColor="#20000000" |
| | | android:type="linear" /> |
| | | </shape> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <shape xmlns:android="http://schemas.android.com/apk/res/android"> |
| | | <gradient |
| | | android:angle="90" |
| | | android:endColor="#00000000" |
| | | android:startColor="#30000000" |
| | | android:type="linear" /> |
| | | </shape> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <shape xmlns:android="http://schemas.android.com/apk/res/android" |
| | | android:shape="rectangle"> |
| | | <solid android:color="#4285F4" /> |
| | | <corners android:radius="2dp" /> |
| | | <size android:height="3dp" android:width="20dp" /> |
| | | </shape> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <ripple xmlns:android="http://schemas.android.com/apk/res/android" |
| | | android:color="#1A4285F4"> |
| | | <item android:id="@android:id/mask"> |
| | | <shape android:shape="rectangle"> |
| | | <solid android:color="#000000" /> |
| | | <corners android:radius="8dp" /> |
| | | </shape> |
| | | </item> |
| | | </ripple> |
| | |
| | | android:orientation="horizontal"> |
| | | |
| | | <LinearLayout |
| | | android:id="@+id/orderLL" |
| | | android:id="@+id/BSCardLL" |
| | | android:layout_width="0dp" |
| | | android:layout_height="50dp" |
| | | android:layout_weight="1" |
| | |
| | | android:layout_height="33dp"> |
| | | |
| | | <ImageView |
| | | android:id="@+id/orderImg" |
| | | android:id="@+id/BSCardImg" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="25dp" |
| | | android:layout_marginTop="8dp" |
| | |
| | | </RelativeLayout> |
| | | |
| | | <TextView |
| | | android:id="@+id/orderText" |
| | | android:id="@+id/BSCardText" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="3dp" |
| | |
| | | </LinearLayout> |
| | | |
| | | <FrameLayout |
| | | android:id="@+id/mapLL" |
| | | android:id="@+id/rechargeLL" |
| | | android:layout_width="0dp" |
| | | android:layout_height="match_parent" |
| | | android:layout_weight="1" |
| | |
| | | android:orientation="vertical"> |
| | | |
| | | <ImageView |
| | | android:id="@+id/mapImg" |
| | | android:id="@+id/rechargeImg" |
| | | android:layout_width="40dp" |
| | | android:layout_height="40dp" |
| | | android:layout_marginTop="8dp" |
| | | android:src="@drawable/bottom_recharge_white" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/mapText" |
| | | android:id="@+id/rechargeText" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="充值" |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:app="http://schemas.android.com/apk/res-auto" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:background="#F5F7FA"> |
| | | |
| | | <com.dayu.baselibrary.view.TitleBar |
| | | android:id="@+id/titleBar" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="@dimen/dimen_title_height" |
| | | android:background="@color/title_bar_bg" |
| | | android:elevation="4dp" |
| | | app:centerText="新卡开户" |
| | | app:leftImage="@mipmap/icon_back" /> |
| | | |
| | | <ScrollView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:layout_above="@+id/newCard_registBtn" |
| | | android:layout_below="@+id/titleBar" |
| | | android:fillViewport="true" |
| | | android:scrollbars="none"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | app:cardCornerRadius="8dp" |
| | | app:cardElevation="2dp"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | android:text="基本信息" |
| | | android:textColor="#333333" |
| | | android:textSize="18sp" |
| | | android:textStyle="bold" /> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | android:gravity="center_vertical" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="@string/eq_no" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/newCard_arerNumber" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginStart="8dp" |
| | | android:inputType="number" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | android:gravity="center_vertical" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:layout_width="80dp" |
| | | android:layout_height="wrap_content" |
| | | android:text="姓名" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_weight="1" |
| | | android:background="#FFFFFF" |
| | | android:orientation="vertical"> |
| | | |
| | | <EditText |
| | | android:id="@+id/newCard_name" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="@null" |
| | | android:hint="请输入姓名" |
| | | android:inputType="textPersonName" |
| | | android:padding="12dp" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | |
| | | <ImageView |
| | | android:id="@+id/newCard_scanBtn" |
| | | android:layout_width="45dp" |
| | | android:layout_height="45dp" |
| | | android:background="?attr/selectableItemBackgroundBorderless" |
| | | android:padding="10dp" |
| | | android:src="@mipmap/icon_scan" |
| | | android:tint="#4285F4" /> |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | android:gravity="center_vertical" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:layout_width="80dp" |
| | | android:layout_height="wrap_content" |
| | | android:text="身份证号" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="#FFFFFF" |
| | | android:orientation="vertical"> |
| | | |
| | | <EditText |
| | | android:id="@+id/newCard_id" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="@null" |
| | | android:digits="0123456789Xx" |
| | | android:hint="请输入身份证号" |
| | | android:inputType="text" |
| | | android:maxLength="18" |
| | | android:padding="12dp" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | </LinearLayout> |
| | | |
| | | <TextView |
| | | android:id="@+id/newCard_idTip" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | android:text="扫描后请核对身份信息无误" |
| | | android:textColor="#FF4500" |
| | | android:visibility="gone" /> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | app:cardCornerRadius="8dp" |
| | | app:cardElevation="2dp"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | android:text="联系方式" |
| | | android:textColor="#333333" |
| | | android:textSize="18sp" |
| | | android:textStyle="bold" /> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | android:gravity="center_vertical" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:layout_width="80dp" |
| | | android:layout_height="wrap_content" |
| | | android:text="电话" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="#FFFFFF" |
| | | android:orientation="vertical"> |
| | | |
| | | <EditText |
| | | android:id="@+id/newCard_phone" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="@null" |
| | | android:hint="请输入手机号码" |
| | | android:inputType="phone" |
| | | android:maxLength="11" |
| | | android:padding="12dp" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | </LinearLayout> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | app:cardCornerRadius="8dp" |
| | | app:cardElevation="2dp"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | android:text="费用信息" |
| | | android:textColor="#333333" |
| | | android:textSize="18sp" |
| | | android:textStyle="bold" /> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center_vertical" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:layout_width="80dp" |
| | | android:layout_height="wrap_content" |
| | | android:text="工本费(元)" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="#FFFFFF" |
| | | android:orientation="vertical"> |
| | | |
| | | <EditText |
| | | android:id="@+id/newCard_morny" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="@null" |
| | | android:hint="请输入工本费" |
| | | android:inputType="numberDecimal" |
| | | android:padding="12dp" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | </LinearLayout> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <!-- 隐藏的水权内水量部分 --> |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:visibility="gone"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="水权内水量:" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | |
| | | <EditText |
| | | android:id="@+id/newCard_water" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:inputType="number" |
| | | android:maxLength="5" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | </LinearLayout> |
| | | </ScrollView> |
| | | |
| | | <TextView |
| | | android:id="@+id/newCard_registBtn" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="56dp" |
| | | android:layout_alignParentBottom="true" |
| | | android:layout_marginStart="16dp" |
| | | android:layout_marginEnd="16dp" |
| | | android:layout_marginBottom="16dp" |
| | | android:background="#4285F4" |
| | | android:gravity="center" |
| | | android:text="确认开户" |
| | | android:textColor="#FFFFFF" |
| | | android:textSize="16sp" /> |
| | | </RelativeLayout> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:app="http://schemas.android.com/apk/res-auto" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:background="@color/white"> |
| | | |
| | | <androidx.appcompat.widget.Toolbar |
| | | android:id="@+id/titleBar" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="@dimen/dimen_title_height" |
| | | android:background="@color/bottom_color" |
| | | android:elevation="4dp" |
| | | app:layout_constraintTop_toTopOf="parent"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_gravity="center" |
| | | android:text="制卡" |
| | | android:textColor="@color/white" |
| | | android:textSize="@dimen/title_text_size" |
| | | android:textStyle="bold" /> |
| | | </androidx.appcompat.widget.Toolbar> |
| | | |
| | | <ScrollView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="0dp" |
| | | android:fillViewport="true" |
| | | app:layout_constraintBottom_toBottomOf="parent" |
| | | app:layout_constraintTop_toBottomOf="@id/titleBar"> |
| | | |
| | | <androidx.constraintlayout.widget.ConstraintLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:padding="16dp"> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:id="@+id/home_newCard" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginStart="8dp" |
| | | android:layout_marginTop="16dp" |
| | | android:layout_marginEnd="8dp" |
| | | android:clickable="true" |
| | | android:focusable="true" |
| | | android:foreground="?android:attr/selectableItemBackground" |
| | | app:cardCornerRadius="12dp" |
| | | app:cardElevation="4dp" |
| | | app:layout_constraintEnd_toStartOf="@+id/home_redCard" |
| | | app:layout_constraintStart_toStartOf="parent" |
| | | app:layout_constraintTop_toTopOf="parent"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <ImageView |
| | | android:layout_width="70dp" |
| | | android:layout_height="70dp" |
| | | android:src="@drawable/home_add" /> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="8dp" |
| | | android:gravity="center" |
| | | android:text="新卡开户" |
| | | android:textColor="@color/text_selecter_color" |
| | | android:textSize="@dimen/home_text_size" /> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:id="@+id/home_recharge" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginStart="8dp" |
| | | android:layout_marginTop="16dp" |
| | | android:layout_marginEnd="8dp" |
| | | android:clickable="true" |
| | | android:focusable="true" |
| | | android:foreground="?android:attr/selectableItemBackground" |
| | | android:visibility="gone" |
| | | app:cardCornerRadius="12dp" |
| | | app:cardElevation="4dp" |
| | | app:layout_constraintEnd_toEndOf="parent" |
| | | app:layout_constraintStart_toStartOf="parent" |
| | | app:layout_constraintTop_toTopOf="parent"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <ImageView |
| | | android:layout_width="70dp" |
| | | android:layout_height="70dp" |
| | | android:src="@drawable/home_recharge" /> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="8dp" |
| | | android:gravity="center" |
| | | android:text="充值" |
| | | android:textColor="@color/text_selecter_color" |
| | | android:textSize="@dimen/home_text_size" /> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:id="@+id/home_redCard" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginStart="8dp" |
| | | android:layout_marginTop="16dp" |
| | | android:layout_marginEnd="8dp" |
| | | android:clickable="true" |
| | | android:focusable="true" |
| | | android:foreground="?android:attr/selectableItemBackground" |
| | | app:cardCornerRadius="12dp" |
| | | app:cardElevation="4dp" |
| | | app:layout_constraintEnd_toEndOf="parent" |
| | | app:layout_constraintStart_toEndOf="@+id/home_newCard" |
| | | app:layout_constraintTop_toTopOf="parent"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <ImageView |
| | | android:layout_width="70dp" |
| | | android:layout_height="70dp" |
| | | android:src="@drawable/home_read" /> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="8dp" |
| | | android:gravity="center" |
| | | android:text="读取卡片" |
| | | android:textColor="@color/text_selecter_color" |
| | | android:textSize="@dimen/home_text_size" /> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:id="@+id/home_loss" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginStart="8dp" |
| | | android:layout_marginTop="24dp" |
| | | android:layout_marginEnd="8dp" |
| | | android:clickable="true" |
| | | android:focusable="true" |
| | | android:foreground="?android:attr/selectableItemBackground" |
| | | app:cardCornerRadius="12dp" |
| | | app:cardElevation="4dp" |
| | | app:layout_constraintEnd_toStartOf="@+id/home_replacement" |
| | | app:layout_constraintStart_toStartOf="parent" |
| | | app:layout_constraintTop_toBottomOf="@+id/home_newCard"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <ImageView |
| | | android:layout_width="70dp" |
| | | android:layout_height="70dp" |
| | | android:src="@drawable/home_report" /> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="8dp" |
| | | android:gravity="center" |
| | | android:text="挂失" |
| | | android:textColor="@color/text_selecter_color" |
| | | android:textSize="@dimen/home_text_size" /> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <TextView |
| | | android:id="@+id/home_reportLoss" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center" |
| | | android:text="解除挂失" |
| | | android:textColor="@color/text_selecter_color" |
| | | android:textSize="@dimen/home_text_size" |
| | | android:visibility="gone" |
| | | app:layout_constraintStart_toStartOf="parent" |
| | | app:layout_constraintTop_toTopOf="parent" /> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:id="@+id/home_replacement" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginStart="8dp" |
| | | android:layout_marginTop="24dp" |
| | | android:layout_marginEnd="8dp" |
| | | android:clickable="true" |
| | | android:focusable="true" |
| | | android:foreground="?android:attr/selectableItemBackground" |
| | | app:cardCornerRadius="12dp" |
| | | app:cardElevation="4dp" |
| | | app:layout_constraintEnd_toEndOf="parent" |
| | | app:layout_constraintStart_toEndOf="@+id/home_loss" |
| | | app:layout_constraintTop_toBottomOf="@+id/home_redCard"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <ImageView |
| | | android:layout_width="70dp" |
| | | android:layout_height="70dp" |
| | | android:src="@drawable/home_replacement" /> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="8dp" |
| | | android:gravity="center" |
| | | android:text="补卡" |
| | | android:textColor="@color/text_selecter_color" |
| | | android:textSize="@dimen/home_text_size" /> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:id="@+id/home_admin" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginStart="8dp" |
| | | android:layout_marginTop="24dp" |
| | | android:layout_marginEnd="8dp" |
| | | android:layout_marginBottom="16dp" |
| | | android:clickable="true" |
| | | android:focusable="true" |
| | | android:foreground="?android:attr/selectableItemBackground" |
| | | app:cardCornerRadius="12dp" |
| | | app:cardElevation="4dp" |
| | | app:layout_constraintBottom_toBottomOf="parent" |
| | | app:layout_constraintEnd_toEndOf="parent" |
| | | app:layout_constraintStart_toStartOf="parent" |
| | | app:layout_constraintTop_toBottomOf="@+id/home_loss" |
| | | app:layout_constraintVertical_bias="0.0"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <ImageView |
| | | android:layout_width="70dp" |
| | | android:layout_height="70dp" |
| | | android:src="@drawable/home_system" /> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="8dp" |
| | | android:gravity="center" |
| | | android:text="管理系统" |
| | | android:textColor="@color/text_selecter_color" |
| | | android:textSize="@dimen/home_text_size" /> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <TextView |
| | | android:id="@+id/home_parameter" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center" |
| | | android:text="参数设置" |
| | | android:textColor="@color/text_selecter_color" |
| | | android:textSize="@dimen/home_text_size" |
| | | android:visibility="gone" |
| | | app:layout_constraintStart_toStartOf="parent" |
| | | app:layout_constraintTop_toTopOf="parent" /> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:id="@+id/home_my" |
| | | android:layout_width="0dp" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginStart="8dp" |
| | | android:layout_marginTop="24dp" |
| | | android:layout_marginEnd="8dp" |
| | | android:clickable="true" |
| | | android:focusable="true" |
| | | android:foreground="?android:attr/selectableItemBackground" |
| | | android:visibility="gone" |
| | | app:cardCornerRadius="12dp" |
| | | app:cardElevation="4dp" |
| | | app:layout_constraintEnd_toEndOf="parent" |
| | | app:layout_constraintStart_toStartOf="parent" |
| | | app:layout_constraintTop_toBottomOf="@+id/home_admin"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:gravity="center" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <ImageView |
| | | android:layout_width="70dp" |
| | | android:layout_height="70dp" |
| | | android:src="@drawable/home_my" /> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginTop="8dp" |
| | | android:gravity="center" |
| | | android:text="个人中心" |
| | | android:textColor="@color/text_selecter_color" |
| | | android:textSize="@dimen/home_text_size" /> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | </androidx.constraintlayout.widget.ConstraintLayout> |
| | | </ScrollView> |
| | | </androidx.constraintlayout.widget.ConstraintLayout> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:app="http://schemas.android.com/apk/res-auto" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:orientation="vertical" |
| | | android:background="#F5F5F5"> |
| | | |
| | | <RelativeLayout |
| | | android:id="@+id/header_layout" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="@dimen/dimen_title_height" |
| | | android:background="@color/bottom_color" |
| | | android:elevation="4dp"> |
| | | |
| | | <TextView |
| | | android:id="@+id/titleBar" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_centerInParent="true" |
| | | android:text="我的" |
| | | android:textColor="@color/white" |
| | | android:textSize="@dimen/title_text_size" |
| | | android:textStyle="bold" /> |
| | | </RelativeLayout> |
| | | |
| | | <ScrollView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:fillViewport="true"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <!-- 用户信息卡片 --> |
| | | <androidx.cardview.widget.CardView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | app:cardCornerRadius="8dp" |
| | | app:cardElevation="2dp" |
| | | app:contentPadding="16dp"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical"> |
| | | |
| | | <TextView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:text="基本信息" |
| | | android:textColor="#333333" |
| | | android:textSize="16sp" |
| | | android:textStyle="bold" |
| | | android:layout_marginBottom="12dp"/> |
| | | |
| | | <LinearLayout |
| | | android:id="@+id/my_village" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="horizontal" |
| | | android:visibility="gone" |
| | | android:layout_marginBottom="8dp"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="设备编号:" |
| | | android:textSize="@dimen/text_size" |
| | | android:textColor="#666666" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/my_villageNum" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:textSize="@dimen/text_size" |
| | | android:textColor="#333333" /> |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="区域名称:" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/my_adName" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" /> |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | | android:id="@+id/my_admin" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="区域号:" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/my_adminName" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" /> |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | | android:id="@+id/my_waterPriceLL" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="电单价(元/度):" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/my_waterPrice" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" /> |
| | | </LinearLayout> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <!-- 记录管理卡片 --> |
| | | <androidx.cardview.widget.CardView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | app:cardCornerRadius="8dp" |
| | | app:cardElevation="2dp" |
| | | app:contentPadding="16dp"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical"> |
| | | |
| | | <TextView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:text="记录管理" |
| | | android:textColor="#333333" |
| | | android:textSize="16sp" |
| | | android:textStyle="bold" |
| | | android:layout_marginBottom="12dp"/> |
| | | |
| | | <RelativeLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="12dp" |
| | | android:padding="8dp" |
| | | android:background="#F9F9F9"> |
| | | |
| | | <ImageView |
| | | android:id="@+id/card_icon" |
| | | android:layout_width="24dp" |
| | | android:layout_height="24dp" |
| | | android:layout_centerVertical="true" |
| | | android:src="@android:drawable/ic_menu_agenda" |
| | | android:tint="#666666" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/my_newCardList_ll" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_centerVertical="true" |
| | | android:layout_marginStart="8dp" |
| | | android:layout_toEndOf="@+id/card_icon" |
| | | android:layout_toStartOf="@+id/my_newCardOut" |
| | | android:text="开卡记录" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/my_newCardOut" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_alignParentEnd="true" |
| | | android:layout_centerVertical="true" |
| | | android:background="@drawable/rounded_button_bg" |
| | | android:paddingStart="12dp" |
| | | android:paddingEnd="12dp" |
| | | android:paddingTop="4dp" |
| | | android:paddingBottom="4dp" |
| | | android:text="导出记录" |
| | | android:textColor="@color/white" |
| | | android:textSize="12sp" /> |
| | | </RelativeLayout> |
| | | |
| | | <RelativeLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:padding="8dp" |
| | | android:background="#F9F9F9"> |
| | | |
| | | <ImageView |
| | | android:id="@+id/recharge_icon" |
| | | android:layout_width="24dp" |
| | | android:layout_height="24dp" |
| | | android:layout_centerVertical="true" |
| | | android:src="@android:drawable/ic_menu_recent_history" |
| | | android:tint="#666666" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/my_rechargeList_ll" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_centerVertical="true" |
| | | android:layout_marginStart="8dp" |
| | | android:layout_toEndOf="@+id/recharge_icon" |
| | | android:layout_toStartOf="@+id/my_rechargeOut" |
| | | android:text="充值记录" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/my_rechargeOut" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_alignParentEnd="true" |
| | | android:layout_centerVertical="true" |
| | | android:background="@drawable/rounded_button_bg" |
| | | android:paddingStart="12dp" |
| | | android:paddingEnd="12dp" |
| | | android:paddingTop="4dp" |
| | | android:paddingBottom="4dp" |
| | | android:text="导出记录" |
| | | android:textColor="@color/white" |
| | | android:textSize="12sp" /> |
| | | </RelativeLayout> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <!-- 设置卡片 --> |
| | | <androidx.cardview.widget.CardView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | app:cardCornerRadius="8dp" |
| | | app:cardElevation="2dp" |
| | | app:contentPadding="16dp"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical"> |
| | | |
| | | <TextView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:text="系统设置" |
| | | android:textColor="#333333" |
| | | android:textSize="16sp" |
| | | android:textStyle="bold" |
| | | android:layout_marginBottom="12dp"/> |
| | | |
| | | <TextView |
| | | android:id="@+id/sys_ip" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="12dp" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:drawableStart="@android:drawable/ic_menu_manage" |
| | | android:drawablePadding="8dp" |
| | | android:drawableTint="#666666" |
| | | android:text="IP设置" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="gone" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/card_replacement" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="12dp" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:drawableStart="@android:drawable/ic_menu_edit" |
| | | android:drawablePadding="8dp" |
| | | android:drawableTint="#666666" |
| | | android:text="用户补卡" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="gone" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/my_psw" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="12dp" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:drawableStart="@android:drawable/ic_lock_lock" |
| | | android:drawablePadding="8dp" |
| | | android:drawableTint="#666666" |
| | | android:text="登录密码设置" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="gone" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/my_testConnect" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:drawableStart="@android:drawable/ic_menu_compass" |
| | | android:drawablePadding="8dp" |
| | | android:drawableTint="#666666" |
| | | android:text="未连接数据中心" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="gone" /> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | </LinearLayout> |
| | | </ScrollView> |
| | | |
| | | <com.wang.avi.AVLoadingIndicatorView |
| | | android:id="@+id/avi" |
| | | style="@style/AVLoadingIndicatorView" |
| | | android:layout_width="80dp" |
| | | android:layout_height="80dp" |
| | | android:layout_centerHorizontal="true" |
| | | android:layout_centerVertical="true" |
| | | android:visibility="gone" |
| | | app:indicatorColor="@color/title_bg" |
| | | app:indicatorName="BallClipRotatePulseIndicator" /> |
| | | |
| | | </LinearLayout> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" |
| | | xmlns:app="http://schemas.android.com/apk/res-auto" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:background="#F5F5F5"> |
| | | |
| | | <RelativeLayout |
| | | android:id="@+id/header_layout" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="@dimen/dimen_title_height" |
| | | android:background="@color/bottom_color" |
| | | android:elevation="4dp"> |
| | | |
| | | <TextView |
| | | android:id="@+id/titleBar" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_centerInParent="true" |
| | | android:text="充值" |
| | | android:textColor="@color/white" |
| | | android:textSize="@dimen/title_text_size" |
| | | android:textStyle="bold" /> |
| | | </RelativeLayout> |
| | | |
| | | <LinearLayout |
| | | android:id="@+id/recharge_read_LL" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:layout_below="@+id/header_layout" |
| | | android:orientation="vertical" |
| | | android:visibility="visible"> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_margin="16dp" |
| | | app:cardCornerRadius="8dp" |
| | | app:cardElevation="2dp"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <TextView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | android:gravity="center" |
| | | android:text="请将卡贴在设备上进行读卡" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:textStyle="bold" /> |
| | | |
| | | <ImageView |
| | | android:layout_width="200dp" |
| | | android:layout_height="200dp" |
| | | android:layout_gravity="center" |
| | | android:scaleType="fitCenter" |
| | | android:src="@mipmap/nfc_write" /> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | </LinearLayout> |
| | | |
| | | <ScrollView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="match_parent" |
| | | android:layout_above="@+id/recharge_registBtn" |
| | | android:layout_below="@+id/header_layout" |
| | | android:fillViewport="true"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <LinearLayout |
| | | android:id="@+id/recharge_text_LL" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:visibility="gone"> |
| | | <androidx.cardview.widget.CardView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | |
| | | app:cardCornerRadius="8dp" |
| | | app:cardElevation="2dp"> |
| | | |
| | | <LinearLayout |
| | | android:id="@+id/recharge_LL" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | |
| | | |
| | | <TextView |
| | | android:id="@+id/red_recharge_water" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:text="充值水量:" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="gone" /> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:orientation="vertical"> |
| | | |
| | | <TextView |
| | | android:id="@+id/recharge_tx" |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:text="充值金额(元):" |
| | | android:textColor="@color/red" |
| | | android:textSize="@dimen/new_card_size" |
| | | android:textStyle="bold" /> |
| | | |
| | | <EditText |
| | | android:id="@+id/recharge_water" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="@android:color/white" |
| | | android:hint="请输入充值的金额" |
| | | android:inputType="numberDecimal" |
| | | android:padding="12dp" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | <androidx.cardview.widget.CardView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | android:layout_marginTop="15dp" |
| | | app:cardCornerRadius="8dp" |
| | | app:cardElevation="2dp"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <TextView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="12dp" |
| | | android:text="卡片信息" |
| | | android:textColor="#333333" |
| | | android:textSize="16sp" |
| | | android:textStyle="bold" /> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="卡状态:" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/red_statu" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:textColor="@color/red" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="姓名:" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/userName" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="卡号:" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/red_initCode" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:text="" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:orientation="horizontal"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="用户编号:" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/red_userCode" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:text="" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:orientation="horizontal" |
| | | android:visibility="visible"> |
| | | |
| | | <TextView |
| | | android:layout_width="wrap_content" |
| | | android:layout_height="wrap_content" |
| | | android:text="充值后剩余金额:" |
| | | android:textColor="#666666" |
| | | android:textSize="@dimen/text_size" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/red_remainder_blance" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/new_card_size" /> |
| | | </LinearLayout> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | <androidx.cardview.widget.CardView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="16dp" |
| | | app:cardCornerRadius="8dp" |
| | | app:cardElevation="2dp"> |
| | | |
| | | <LinearLayout |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:orientation="vertical" |
| | | android:padding="16dp"> |
| | | |
| | | <TextView |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="12dp" |
| | | android:text="电量信息" |
| | | android:textColor="#333333" |
| | | android:textSize="16sp" |
| | | android:textStyle="bold" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/red_balance_electric" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:text="剩余电量:" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="visible" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/red_rechargeDate" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:text="最后购水日期:" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="visible" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/red_rechargeElectric" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:text="电量单价:" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="visible" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/red_rechargeNumber" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:text="充值次数:" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="gone" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/red_total_water" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:text="总用水量:" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="gone" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/red_total_power" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:layout_marginBottom="8dp" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:text="总用电量:" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="gone" /> |
| | | |
| | | <TextView |
| | | android:id="@+id/red_waterPrice" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="wrap_content" |
| | | android:background="#F9F9F9" |
| | | android:padding="12dp" |
| | | android:text="每立方米水价格:" |
| | | android:textColor="#333333" |
| | | android:textSize="@dimen/text_size" |
| | | android:visibility="gone" /> |
| | | </LinearLayout> |
| | | </androidx.cardview.widget.CardView> |
| | | |
| | | |
| | | </LinearLayout> |
| | | </LinearLayout> |
| | | </ScrollView> |
| | | |
| | | <Button |
| | | android:id="@+id/recharge_registBtn" |
| | | android:layout_width="match_parent" |
| | | android:layout_height="50dp" |
| | | android:layout_alignParentBottom="true" |
| | | android:layout_margin="16dp" |
| | | android:background="@drawable/rounded_button_bg" |
| | | android:elevation="2dp" |
| | | android:gravity="center" |
| | | android:text="充值" |
| | | android:textColor="@color/white" |
| | | android:textSize="@dimen/new_card_size" |
| | | android:textStyle="bold" |
| | | android:visibility="gone" /> |
| | | </RelativeLayout> |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <resources> |
| | | <color name="bottom_color">#3D8BFF</color> |
| | | <color name="black">#333</color> |
| | | <color name="white">#fff</color> |
| | | <color name="nav_item_color">#555555</color> |
| | | |
| | | </resources> |
New file |
| | |
| | | <?xml version="1.0" encoding="utf-8"?> |
| | | <resources> |
| | | |
| | | <dimen name="title_text_size">20sp</dimen> |
| | | |
| | | </resources> |
| | |
| | | |
| | | import android.app.Activity; |
| | | import android.content.Intent; |
| | | import android.nfc.NfcAdapter; |
| | | import android.nfc.Tag; |
| | | import android.nfc.tech.MifareClassic; |
| | | import android.util.Log; |
| | | |
| | | import com.dayu.baselibrary.tools.HexUtil; |
| | | import com.dayu.baselibrary.tools.nfc.BaseNfcWriteHelper; |
| | | import com.dayu.baselibrary.tools.nfc.NfcWriteAdapter; |
| | | import com.dayu.henanlibrary.card.UserCardHN; |
| | | |
| | | import java.io.IOException; |
| | | |
| | | /** |
| | | * @author zx |