xuqm преди 1 година
родител
ревизия
00f97d1ed1
променени са 100 файла, в които са добавени 6987 реда и са изтрити 0 реда
  1. 85 0
      .gitignore
  2. 1 0
      README.md
  3. 1 0
      app/.gitignore
  4. 85 0
      app/build.gradle
  5. 21 0
      app/proguard-rules.pro
  6. 76 0
      app/src/main/AndroidManifest.xml
  7. BIN
      app/src/main/assets/fonts/DIN-Alternate-Bold.ttf
  8. BIN
      app/src/main/assets/fonts/SourceHanSansCN-Bold.ttf
  9. BIN
      app/src/main/assets/fonts/SourceHanSansCN-Medium.ttf
  10. BIN
      app/src/main/assets/fonts/SourceHanSansCN-Normal.otf
  11. BIN
      app/src/main/assets/fonts/SourceHanSansCN-Normal.ttf
  12. BIN
      app/src/main/assets/fonts/SourceHanSansCN-Regular.ttf
  13. 33 0
      app/src/main/java/com/bjca/hp/acupuncture/MyApplication.java
  14. 166 0
      app/src/main/java/com/bjca/hp/acupuncture/common/CrashHandler.java
  15. 248 0
      app/src/main/java/com/bjca/hp/acupuncture/common/RabbitMQClient.java
  16. 239 0
      app/src/main/java/com/bjca/hp/acupuncture/common/RabbitMQUtil.java
  17. 4 0
      app/src/main/java/com/bjca/hp/acupuncture/common/SharedPreferencesConfigs.kt
  18. 72 0
      app/src/main/java/com/bjca/hp/acupuncture/model/DrugUsage.kt
  19. 71 0
      app/src/main/java/com/bjca/hp/acupuncture/model/ItemsItem.kt
  20. 15 0
      app/src/main/java/com/bjca/hp/acupuncture/model/MqMessage.kt
  21. 168 0
      app/src/main/java/com/bjca/hp/acupuncture/model/RegModel.kt
  22. 153 0
      app/src/main/java/com/bjca/hp/acupuncture/model/RpDetailModel.kt
  23. 21 0
      app/src/main/java/com/bjca/hp/acupuncture/model/RpModel.kt
  24. 113 0
      app/src/main/java/com/bjca/hp/acupuncture/model/WelcomeLIstModel.kt
  25. 30 0
      app/src/main/java/com/bjca/hp/acupuncture/receiver/BootCompleteReceiver.kt
  26. 130 0
      app/src/main/java/com/bjca/hp/acupuncture/repository/HeaderInterceptor.kt
  27. 30 0
      app/src/main/java/com/bjca/hp/acupuncture/repository/Service.kt
  28. 243 0
      app/src/main/java/com/bjca/hp/acupuncture/ui/MainActivity.kt
  29. 209 0
      app/src/main/java/com/bjca/hp/acupuncture/ui/SettingActivity.kt
  30. 36 0
      app/src/main/java/com/bjca/hp/acupuncture/ui/TestActivity.kt
  31. 166 0
      app/src/main/java/com/bjca/hp/acupuncture/ui/WelcomeActivity.kt
  32. 104 0
      app/src/main/java/com/bjca/hp/acupuncture/viewmodel/MainVM.kt
  33. 40 0
      app/src/main/java/com/bjca/hp/acupuncture/viewmodel/WelcomeVM.kt
  34. 22 0
      app/src/main/java/com/bjca/hp/acupuncture/widget/ColorUser.java
  35. 11 0
      app/src/main/res/drawable/bg_item_welcome.xml
  36. 10 0
      app/src/main/res/drawable/bg_main.xml
  37. 10 0
      app/src/main/res/drawable/bg_main_btn.xml
  38. 10 0
      app/src/main/res/drawable/bg_main_item_content.xml
  39. 10 0
      app/src/main/res/drawable/bg_main_item_index.xml
  40. 10 0
      app/src/main/res/drawable/bg_main_tag.xml
  41. 698 0
      app/src/main/res/layout/activity_main.xml
  42. 167 0
      app/src/main/res/layout/activity_setting.xml
  43. 30 0
      app/src/main/res/layout/activity_test.xml
  44. 50 0
      app/src/main/res/layout/activity_welcome.xml
  45. 224 0
      app/src/main/res/layout/item_main.xml
  46. 83 0
      app/src/main/res/layout/item_welcome.xml
  47. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.png
  48. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.png
  49. BIN
      app/src/main/res/mipmap-xhdpi/ic_face.png
  50. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.png
  51. BIN
      app/src/main/res/mipmap-xhdpi/ic_psw.png
  52. BIN
      app/src/main/res/mipmap-xxhdpi/bg_login.jpg
  53. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.png
  54. BIN
      app/src/main/res/mipmap-xxhdpi/icon_arrow_blue.png
  55. BIN
      app/src/main/res/mipmap-xxhdpi/icon_login.png
  56. BIN
      app/src/main/res/mipmap-xxhdpi/login_bg.jpg
  57. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
  58. 3 0
      app/src/main/res/values/arrays.xml
  59. 12 0
      app/src/main/res/values/colors.xml
  60. 3 0
      app/src/main/res/values/strings.xml
  61. 20 0
      app/src/main/res/values/styles.xml
  62. 6 0
      app/src/main/res/xml/filepaths.xml
  63. 2 0
      base/.gitattributes
  64. 83 0
      base/.gitignore
  65. 184 0
      base/README.md
  66. 94 0
      base/build.gradle
  67. 0 0
      base/consumer-rules.pro
  68. 21 0
      base/proguard-rules.pro
  69. 27 0
      base/src/main/AndroidManifest.xml
  70. 155 0
      base/src/main/java/com/livinglifetechway/quickpermissions_kotlin/PermissionsManager.kt
  71. 237 0
      base/src/main/java/com/livinglifetechway/quickpermissions_kotlin/util/PermissionCheckerFragment.kt
  72. 54 0
      base/src/main/java/com/livinglifetechway/quickpermissions_kotlin/util/PermissionsUtil.kt
  73. 11 0
      base/src/main/java/com/livinglifetechway/quickpermissions_kotlin/util/QuickPermissionsOptions.kt
  74. 30 0
      base/src/main/java/com/livinglifetechway/quickpermissions_kotlin/util/QuickPermissionsRequest.kt
  75. 111 0
      base/src/main/java/com/xuqm/base/App.java
  76. 222 0
      base/src/main/java/com/xuqm/base/CrashHandler.java
  77. 17 0
      base/src/main/java/com/xuqm/base/adapter/BaseItem.java
  78. 186 0
      base/src/main/java/com/xuqm/base/adapter/BaseNormalAdapter.java
  79. 157 0
      base/src/main/java/com/xuqm/base/adapter/BasePagedAdapter.java
  80. 59 0
      base/src/main/java/com/xuqm/base/adapter/CommonAdapter.java
  81. 36 0
      base/src/main/java/com/xuqm/base/adapter/CommonPagedAdapter.java
  82. 16 0
      base/src/main/java/com/xuqm/base/adapter/Diff.java
  83. 72 0
      base/src/main/java/com/xuqm/base/adapter/ElasticHorizontalScrollView.java
  84. 30 0
      base/src/main/java/com/xuqm/base/adapter/FragmentAdapter.java
  85. 15 0
      base/src/main/java/com/xuqm/base/adapter/ItemViewDelegate.java
  86. 95 0
      base/src/main/java/com/xuqm/base/adapter/ItemViewDelegateManager.java
  87. 178 0
      base/src/main/java/com/xuqm/base/adapter/SlipReAdapter.java
  88. 156 0
      base/src/main/java/com/xuqm/base/adapter/ViewHolder.java
  89. 10 0
      base/src/main/java/com/xuqm/base/adapter/callback/AdapterClickListener.java
  90. 11 0
      base/src/main/java/com/xuqm/base/adapter/callback/AdapterItemClickListener.java
  91. 11 0
      base/src/main/java/com/xuqm/base/adapter/callback/AdapterItemLongClickListener.java
  92. 94 0
      base/src/main/java/com/xuqm/base/common/AppManager.java
  93. 193 0
      base/src/main/java/com/xuqm/base/common/FileHelper.java
  94. 230 0
      base/src/main/java/com/xuqm/base/common/GlideEngine.kt
  95. 54 0
      base/src/main/java/com/xuqm/base/common/GsonImplHelp.java
  96. 65 0
      base/src/main/java/com/xuqm/base/common/ImageHelp.java
  97. 20 0
      base/src/main/java/com/xuqm/base/common/ImageHelper.java
  98. 29 0
      base/src/main/java/com/xuqm/base/common/Json.java
  99. 75 0
      base/src/main/java/com/xuqm/base/common/LogHelper.java
  100. 8 0
      base/src/main/java/com/xuqm/base/common/RefreshResult.java

+ 85 - 0
.gitignore

@@ -0,0 +1,85 @@
+# Built application files
+*.apk
+*.aar
+*.ap_
+*.aab
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+#  Uncomment the following line in case you need and you don't have the release build type files in your app
+# release/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# IntelliJ
+*.iml
+.idea/
+# Android Studio 3 in .gitignore file.
+.idea/caches
+.idea/modules.xml
+# Comment next line if keeping position of elements in Navigation Editor is relevant for you
+.idea/navEditor.xml
+
+# Keystore files
+# Uncomment the following lines if you do not want to check your keystore files in.
+#*.jks
+#*.keystore
+
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
+.cxx/
+
+# Google Services (e.g. APIs or Firebase)
+# google-services.json
+
+# Freeline
+freeline.py
+freeline/
+freeline_project_description.json
+
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md
+
+# Version control
+vcs.xml
+
+# lint
+lint/intermediates/
+lint/generated/
+lint/outputs/
+lint/tmp/
+# lint/reports/
+
+/app/key
+output-metadata.json
+# Android Profiling
+*.hprof

+ 1 - 0
README.md

@@ -0,0 +1 @@
+# 摆药机

+ 1 - 0
app/.gitignore

@@ -0,0 +1 @@
+/build

+ 85 - 0
app/build.gradle

@@ -0,0 +1,85 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+
+
+android {
+    compileSdkVersion versions.compileSdk
+    buildToolsVersion versions.buildTools
+
+    defaultConfig {
+        applicationId apps.applicationId
+        minSdkVersion versions.minSdk
+        targetSdkVersion versions.targetSdk
+        versionCode versions.versionCode
+        versionName versions.versionName
+
+        manifestPlaceholders = [
+                APP_NAME: apps.applicationName,
+                APP_ID  : apps.applicationId,
+        ]
+        buildConfigField("String", "APP_Name", "\"" + apps.applicationName + "\"")
+
+        flavorDimensions "versioncode"
+    }
+    buildTypes {
+        debug {
+            minifyEnabled false
+            signingConfig signingConfigs.debug
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+        release {
+            minifyEnabled false
+            signingConfig signingConfigs.debug
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    applicationVariants.all { variant ->
+        variant.outputs.all { output ->
+            if ("debug" != variant.buildType.name) {
+                def now = new Date()
+                def path = "../../../../../apks/${variant.buildType.name}/v${defaultConfig.versionName}_" + now.format("yyyy.MM.dd_HH")
+                outputFileName = path + "/${applicationId}.apk"
+            }
+        }
+    }
+    signingConfigs {
+        debug {
+            keyAlias 'xuqm'
+            keyPassword 'xuqinmin1022'
+            storeFile file('key')
+            storePassword 'xuqinmin1022'
+        }
+        releaseConfig {}
+    }
+
+    sourceSets {
+        main {
+            jniLibs.srcDirs = ['libs']
+        }
+    }
+    androidExtensions {
+        experimental = true
+    }
+    namespace 'com.bjca.hp.acupuncture'
+}
+
+dependencies {
+    implementation fileTree(dir: "libs", include: ["*.jar","*.aar"])
+    implementation project(path: ':core')
+    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+
+    implementation 'com.android.support:appcompat-v7:28.0.0'
+    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+    implementation 'androidx.appcompat:appcompat:1.3.0'
+    implementation 'com.google.android.material:material:1.3.0'
+
+    implementation 'tp.xmaihh:serialport:2.1'
+
+    implementation 'com.rabbitmq:amqp-client:5.15.0'
+
+
+}

+ 21 - 0
app/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 76 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools">
+    <!-- 网络权限 -->
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <!-- <uses-permission android:name="android.permission.READ_LOGS" /> -->
+    <uses-feature android:name="android.hardware.camera" />
+    <uses-feature android:name="android.hardware.camera.autofocus" />
+
+    <application
+        android:name=".MyApplication"
+        android:allowBackup="true"
+        android:icon="@mipmap/ic_launcher"
+        android:label="${APP_NAME}"
+        android:supportsRtl="true"
+        android:theme="@style/AppTheme"
+        android:usesCleartextTraffic="true"
+        tools:replace="android:label">
+        <activity
+            android:name=".ui.TestActivity"
+            android:exported="false" />
+        <activity
+            android:name=".ui.SettingActivity"
+            android:exported="false" />
+
+        <receiver
+            android:name=".receiver.BootCompleteReceiver"
+            android:enabled="true"
+            android:exported="true">
+
+            <!-- 接收启动完成的广播 -->
+            <intent-filter android:priority="1000">
+                <action android:name="android.intent.action.BOOT_COMPLETED" />
+            </intent-filter>
+        </receiver>
+
+        <activity
+            android:name=".ui.MainActivity"
+            android:exported="false" />
+        <activity
+            android:name=".ui.WelcomeActivity"
+            android:exported="true"
+            android:screenOrientation="portrait">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+
+        <provider
+            android:name="androidx.core.content.FileProvider"
+            android:authorities="${APP_ID}.fileprovider"
+            android:exported="false"
+            android:grantUriPermissions="true">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/filepaths" />
+        </provider>
+
+        <meta-data
+            android:name="design_width_in_dp"
+            android:value="540" />
+        <meta-data
+            android:name="design_height_in_dp"
+            android:value="960" />
+    </application>
+
+</manifest>

BIN
app/src/main/assets/fonts/DIN-Alternate-Bold.ttf


BIN
app/src/main/assets/fonts/SourceHanSansCN-Bold.ttf


BIN
app/src/main/assets/fonts/SourceHanSansCN-Medium.ttf


BIN
app/src/main/assets/fonts/SourceHanSansCN-Normal.otf


BIN
app/src/main/assets/fonts/SourceHanSansCN-Normal.ttf


BIN
app/src/main/assets/fonts/SourceHanSansCN-Regular.ttf


+ 33 - 0
app/src/main/java/com/bjca/hp/acupuncture/MyApplication.java

@@ -0,0 +1,33 @@
+package com.bjca.hp.acupuncture;
+
+import com.xuqm.base.App;
+import com.xuqm.base.di.component.AppComponent;
+import com.xuqm.base.di.manager.HttpManager;
+import com.bjca.hp.acupuncture.common.CrashHandler;
+import com.bjca.hp.acupuncture.repository.HeaderInterceptor;
+
+/**
+ * @author xuqm
+ */
+public class MyApplication extends App {
+
+    public static String baseUrl = "http://10.10.203.120:31734";
+    public static AppComponent appComponent1;
+    public static AppComponent appComponent2;
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        appComponent = HttpManager.getAppComponent(baseUrl, new HeaderInterceptor(getApplicationContext()));
+        appComponent1 = HttpManager.getAppComponent("http://10.10.203.120:35662", new HeaderInterceptor(getApplicationContext()));
+        appComponent2 = HttpManager.getAppComponent("http://10.10.203.120:31669", new HeaderInterceptor(getApplicationContext()));
+
+        CrashHandler.getInstance().init(this);
+        
+    }
+
+    @Override
+    public boolean showLog() {
+        return super.showLog();
+    }
+}

+ 166 - 0
app/src/main/java/com/bjca/hp/acupuncture/common/CrashHandler.java

@@ -0,0 +1,166 @@
+package com.bjca.hp.acupuncture.common;
+
+import android.content.Context;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+/*************************************************************************************************
+ * <pre>
+ * @包路径: cn.org.bjca.wcert.ywq.utils.crash
+ * @版权所有: 北京数字认证股份有限公司 (C) 2017
+ *
+ * @类描述:
+ * @版本: V1.5.1
+ * @作者 daizhenhong
+ * @创建时间 2017/12/13 下午4:22
+ *
+ * @修改记录:
+-----------------------------------------------------------------------------------------------
+----------- 时间      |   修改人    |     修改的方法       |         修改描述   ---------------
+-----------------------------------------------------------------------------------------------
+</pre>
+ ************************************************************************************************/
+public class CrashHandler implements Thread.UncaughtExceptionHandler {
+
+
+    private static String TAG = "CrashHandler";
+    // 系统默认的UncaughtException处理类
+    private Thread.UncaughtExceptionHandler mDefaultHandler;
+    // 用于格式化日期,作为日志文件名的一部分
+    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+    private static CrashHandler mCrashHandler;
+    private Context mContext;
+
+
+    private CrashHandler() {
+
+    }
+
+    public static CrashHandler getInstance() {
+        if (mCrashHandler == null) {
+            synchronized (CrashHandler.class) {
+                mCrashHandler = new CrashHandler();
+            }
+        }
+        return mCrashHandler;
+    }
+
+    public void init(Context context) {
+        mContext = context;
+        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+        Thread.setDefaultUncaughtExceptionHandler(this);
+    }
+
+    @Override
+    public void uncaughtException(Thread thread, Throwable throwable) {
+        if (!handleException(throwable) && mDefaultHandler != null) {
+            // 如果用户没有处理则让系统默认的异常处理器来处理
+            mDefaultHandler.uncaughtException(thread, throwable);
+        } else {
+            SystemClock.sleep(2000);
+            // 退出程序
+            android.os.Process.killProcess(android.os.Process.myPid());
+            System.exit(1);
+        }
+    }
+
+
+    private boolean handleException(Throwable ex) {
+        if (ex == null) {
+            return false;
+        }
+        try {
+            saveCrashInfoFile(ex);
+            SystemClock.sleep(2000);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return true;
+    }
+
+    private String saveCrashInfoFile(Throwable ex) {
+        StringBuffer sb = new StringBuffer();
+
+        if (hasSdcard())
+            try {
+                SimpleDateFormat sDateFormat = new SimpleDateFormat(
+                        "yyyy-MM-dd HH:mm:ss", Locale.CHINA);
+                String date = sDateFormat.format(new Date());
+                sb.append("\r\n" + date + "\n");
+
+                Writer writer = new StringWriter();
+                PrintWriter printWriter = new PrintWriter(writer);
+                ex.printStackTrace(printWriter);
+                Throwable cause = ex.getCause();
+                while (cause != null) {
+                    cause.printStackTrace(printWriter);
+                    cause = cause.getCause();
+                }
+                printWriter.flush();
+                printWriter.close();
+                String result = writer.toString();
+                sb.append(result);
+
+                String fileName = writeFile(sb.toString());
+                return fileName;
+            } catch (Exception e) {
+                Log.e(TAG, "an error occured while writing file...", e);
+                sb.append("an error occured while writing file...\r\n");
+                writeFile(sb.toString());
+            }
+        return null;
+    }
+
+    private String writeFile(String text) {
+        Log.e("writeFile", text);
+        String time = formatter.format(new Date());
+        String fileName = "crash-demo-" + time + ".log";
+
+        String path = getGlobalpath();
+
+        File dir = new File(path);
+        if (!dir.exists()) {
+            dir.mkdir();
+        }
+        FileOutputStream fos = null;
+        try {
+            fos = new FileOutputStream(path + fileName, true);
+            fos.write(text.getBytes());
+            fos.flush();
+            fos.close();
+
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return fileName;
+    }
+
+
+    public String getGlobalpath() {
+        return Environment.getExternalStorageDirectory().getAbsolutePath()
+                + File.separator + "crash" + File.separator;
+    }
+
+
+    public boolean hasSdcard(){
+        return Environment.getExternalStorageState()
+                .equals(Environment.MEDIA_MOUNTED);
+    }
+}
+

+ 248 - 0
app/src/main/java/com/bjca/hp/acupuncture/common/RabbitMQClient.java

@@ -0,0 +1,248 @@
+package com.bjca.hp.acupuncture.common;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.rabbitmq.client.AMQP;
+import com.rabbitmq.client.AlreadyClosedException;
+import com.rabbitmq.client.Channel;
+import com.rabbitmq.client.Connection;
+import com.rabbitmq.client.ConnectionFactory;
+import com.rabbitmq.client.DefaultConsumer;
+import com.rabbitmq.client.Envelope;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+/***************************************************************************
+ * <pre></pre>
+ * @文件名称: RabbitMQClient
+ * @包 路   径:  com.bjca.hp.acupuncture.common
+ * @版权所有:北京数字医信责任有限公司 (C) 2022
+ *
+ * @类描述:
+ * @版本: V1.0
+ * @创建人: xuqm
+ * @创建时间:2022/6/15 下午 02:19
+ * @修改记录:
+ */
+public class RabbitMQClient {
+    private final String TAG = "RabbitMQ";
+    private final String FLAG_SEND = "send";
+    private final String FLAG_RECEIVE = "receive";
+
+    private final ConnectionFactory factory;
+    private Connection connection;
+    private Map<String, Channel> channelMap = new HashMap<>();
+
+    public static final String EXCHANGETYPE_FANOUT = "fanout";   //不用匹配路由,发送给所有绑定转换器的队列
+    public static final String EXCHANGETYPE_DIRECT = "direct";  //匹配路由一致,才发送给绑定转换器队列
+    public static final String EXCHANGETYPE_TOPIC = "topic";  // 通配符* 和 # 匹配路由一致,才发送给绑定转换器队列
+
+
+    public RabbitMQClient(String hostIp, int port, String username, String password) {
+        factory = new ConnectionFactory();
+        factory.setUsername(username);
+        factory.setPassword(password);
+        factory.setHost(hostIp);
+        factory.setPort(port);
+        factory.setVirtualHost("/");//类似数据库的意思
+        factory.setConnectionTimeout(15 * 1000);         //连接时间设置为10秒
+        factory.setAutomaticRecoveryEnabled(true);   //恢复连接,通道
+        factory.setTopologyRecoveryEnabled(true);    //恢复通道中 转换器,队列,绑定关系等
+        factory.setNetworkRecoveryInterval(5 * 1000);    //恢复连接间隔,默认5秒
+    }
+
+
+    /**
+     * @param message   需要发送的消息
+     * @param queueName 管道名称
+     * @date 创建时间:2020/9/8 0008
+     * @auther gaoxiaoxiong
+     * @Descriptiion
+     **/
+    public void sendQueueMessage(String message, String queueName) throws IOException, TimeoutException, AlreadyClosedException {
+        if (connection == null || !connection.isOpen()) {
+            connection = factory.newConnection();
+        }
+        if (!channelMap.containsKey(FLAG_SEND + queueName)) {
+            Channel channel = connection.createChannel();
+            channel.queueDeclare(queueName, false, false, false, null);
+            channelMap.put(FLAG_SEND + queueName, channel);
+        }
+        //空名字的交换机,需要设置routingKey,此时会将routingKey 作为 队列名使用
+        channelMap.get(FLAG_SEND + queueName).basicPublish("", queueName, null, message.getBytes());
+    }
+
+
+    /**
+     * @param exchangeName 交换机名称
+     * @param message      需要发送的消息
+     * @param queueName    队列名称
+     * @param routingKey   路由规则
+     * @date 创建时间:2020/9/8 0008
+     * @auther gaoxiaoxiong
+     * @Descriptiion 发送 exchangeType direct 类型的信息
+     **/
+    public void sendDirectTypeMessage(String exchangeName, String message, String queueName, String routingKey) throws IOException, TimeoutException, AlreadyClosedException {
+        if (connection == null || !connection.isOpen()) {
+            connection = factory.newConnection();
+        }
+        if (!channelMap.containsKey(FLAG_SEND + exchangeName + EXCHANGETYPE_DIRECT + queueName)) {
+            Channel channel = connection.createChannel();
+            channel.queueDeclare(queueName, false, false, false, null);
+            channel.exchangeDeclare(exchangeName, EXCHANGETYPE_DIRECT);
+            channelMap.put(FLAG_SEND + exchangeName + EXCHANGETYPE_DIRECT + queueName, channel);
+        }
+        channelMap.get(FLAG_SEND + exchangeName + EXCHANGETYPE_DIRECT + queueName).basicPublish(exchangeName, routingKey, null, message.getBytes());
+    }
+
+    /**
+     * @param exchangeName 交换机名称
+     * @param queueName    队列名称
+     * @param message      发送的消息
+     * @date 创建时间:2020/9/8 0008
+     * @auther gaoxiaoxiong
+     * @Descriptiion 发送 exchangeType fanout 类型的信息
+     **/
+    public void sendFanoutTypeMessage(String exchangeName, String queueName, String message) throws IOException, TimeoutException, AlreadyClosedException {
+        if (connection == null || !connection.isOpen()) {
+            connection = factory.newConnection();
+        }
+        if (!channelMap.containsKey(FLAG_SEND + exchangeName + EXCHANGETYPE_FANOUT + queueName)) {
+            Channel channel = connection.createChannel();
+            channel.queueDeclare(queueName, false, false, false, null);
+            channel.exchangeDeclare(exchangeName, EXCHANGETYPE_FANOUT);
+            channelMap.put(FLAG_SEND + exchangeName + EXCHANGETYPE_FANOUT + queueName, channel);
+        }
+        channelMap.get(FLAG_SEND + exchangeName + EXCHANGETYPE_FANOUT + queueName).basicPublish(exchangeName, "", null, message.getBytes());
+    }
+
+    /**
+     * @param exchangeName 交换机名称
+     * @param exchangeType 模式
+     * @param queueName    队列名称
+     * @param message      需要发送的消息
+     * @param routingKey   路由规则
+     * @date 创建时间:2020/9/8 0008
+     * @auther gaoxiaoxiong
+     * @Descriptiion
+     **/
+    public void sendExchangeNameQueueMessage(String exchangeName, String exchangeType, String message, String queueName, String routingKey) throws IOException, TimeoutException, AlreadyClosedException {
+        if (connection == null || !connection.isOpen()) {
+            connection = factory.newConnection();
+        }
+        if (!channelMap.containsKey(FLAG_SEND + exchangeName + exchangeType + queueName)) {
+            Channel channel = connection.createChannel();
+            channel.queueDeclare(queueName, false, false, false, null);
+            channel.exchangeDeclare(exchangeName, exchangeType);
+            channelMap.put(FLAG_SEND + exchangeName + exchangeType + queueName, channel);
+        }
+        if (exchangeType.equals(EXCHANGETYPE_FANOUT)) {
+            channelMap.get(FLAG_SEND + exchangeName + exchangeType + queueName).basicPublish(exchangeName, "", null, message.getBytes());
+        } else if (exchangeType.equals(EXCHANGETYPE_DIRECT)) {
+            channelMap.get(FLAG_SEND + exchangeName + exchangeType + queueName).basicPublish(exchangeName, routingKey, null, message.getBytes());
+        } else if (exchangeType.equals(EXCHANGETYPE_TOPIC)) {
+            channelMap.get(FLAG_SEND + exchangeName + exchangeType + queueName).basicPublish(exchangeName, routingKey, null, message.getBytes());
+        }
+    }
+
+
+    /**
+     * @param queueName 队列名称
+     * @date 创建时间:2020/9/8 0008
+     * @auther gaoxiaoxiong
+     * @Descriptiion
+     **/
+    public void receiveQueueMessage(final String queueName, final ResponseListener listener)
+            throws IOException, TimeoutException, AlreadyClosedException {
+        receiveQueueRoutingKeyMessage(queueName, "", "", "", listener);
+    }
+
+
+    /**
+     * @param queueName    队列名称
+     * @param routingKey   路由规则
+     * @param exchangeName 交换机名称
+     * @param exchangeType 交换机类型
+     * @date 创建时间:2020/9/8 0008
+     * @auther gaoxiaoxiong
+     * @Descriptiion
+     **/
+    public void receiveQueueRoutingKeyMessage(String queueName, final String routingKey, String exchangeName, String exchangeType, final ResponseListener listener)
+            throws IOException, TimeoutException, AlreadyClosedException {
+
+        if (exchangeType.equals(EXCHANGETYPE_DIRECT) || exchangeType.equals(EXCHANGETYPE_TOPIC)) {
+            if (TextUtils.isEmpty(routingKey)) {
+                throw new NullPointerException("路由规则不能为空");
+            }
+        }
+
+        if (!TextUtils.isEmpty(routingKey)) {
+            if (TextUtils.isEmpty(exchangeName)) {
+                throw new NullPointerException("交换机名称不能为空");
+            }
+        }
+
+        if (!channelMap.containsKey(FLAG_RECEIVE + routingKey + queueName)) {
+            if (connection == null || !connection.isOpen()) {
+                connection = factory.newConnection();
+            }
+
+            final Channel channel = connection.createChannel();
+            channel.queueDeclare(queueName, true, false, false, null);
+            //绑定转换器,使用路由筛选消息
+            if (!TextUtils.isEmpty(routingKey)) {
+                channel.exchangeDeclare(exchangeName, exchangeType);
+                channel.queueBind(queueName, exchangeName, routingKey);  //设置绑定
+            }
+            //监听队列
+            channel.basicConsume(queueName, false, new DefaultConsumer(channel) {
+                @Override
+                public void handleDelivery(String consumerTag, Envelope envelope,
+                                           AMQP.BasicProperties properties, byte[] body)
+                        throws IOException {
+                    String message = new String(body, "UTF-8");
+                    if (listener != null) {
+                        listener.receive(message);
+                    }
+                    channel.basicAck(envelope.getDeliveryTag(), false);  //消息应答
+                }
+            });
+            channelMap.put(FLAG_RECEIVE + routingKey + queueName, channel);
+            Log.e(TAG,"已经连接上了,队列名称:" + queueName);
+        }
+    }
+
+
+    /**
+     * 关闭所有资源
+     */
+    public void close() {
+        for (Channel next : channelMap.values()) {
+            if (next != null && next.isOpen()) {
+                try {
+                    next.close();
+                } catch (IOException | TimeoutException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+        channelMap.clear();
+        if (connection != null && connection.isOpen()) {
+            try {
+                connection.close();
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+
+    public interface ResponseListener {
+        void receive(String message);
+    }
+}
+

+ 239 - 0
app/src/main/java/com/bjca/hp/acupuncture/common/RabbitMQUtil.java

@@ -0,0 +1,239 @@
+package com.bjca.hp.acupuncture.common;
+
+import android.os.SystemClock;
+import android.text.TextUtils;
+
+import com.rabbitmq.client.AlreadyClosedException;
+
+import java.io.IOException;
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeoutException;
+
+/***************************************************************************
+ * <pre></pre>
+ * @文件名称: RabbitMQUtil
+ * @包 路   径:  com.bjca.hp.acupuncture.common
+ * @版权所有:北京数字医信责任有限公司 (C) 2022
+ *
+ * @类描述:
+ * @版本: V1.0
+ * @创建人: xuqm
+ * @创建时间:2022/6/15 下午 02:22
+ * @修改记录:
+ */
+public class RabbitMQUtil {
+    private boolean isRunning = true;
+    private RabbitMQClient rabbitMQ;
+    private ExecutorService executor;
+
+
+    public RabbitMQUtil(String hostIp, int port, String username, String password) {
+        rabbitMQ = new RabbitMQClient(hostIp, port, username, password);
+        executor = Executors.newSingleThreadExecutor();  //根据项目需要设置常用线程个数
+    }
+
+    /**
+     * @param message   发送的消息
+     * @param queueName 队列名称
+     * @date 创建时间:2020/9/8 0008
+     * @auther gaoxiaoxiong
+     * @Descriptiion
+     **/
+    public void sendMessage(final String message, final String queueName, final SendMessageListener sendMessageListener,final ErrorMessageListener errorMessageListener) {
+        executor.execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    rabbitMQ.sendQueueMessage(message, queueName);
+                    if (sendMessageListener != null) sendMessageListener.sendMessage(true);
+                } catch (IOException | TimeoutException | AlreadyClosedException e) {
+                    e.printStackTrace();
+                    if (errorMessageListener!=null){
+                        errorMessageListener.errorMessage(e);
+                    }
+                    if (sendMessageListener != null) sendMessageListener.sendMessage(false);
+                }
+            }
+        });
+    }
+
+    /**
+     * @param message      发送的消息
+     * @param exchangeName 交换机名称
+     * @param queueName    队列名称
+     * @date 创建时间:2020/9/8 0008
+     * @auther gaoxiaoxiong
+     * @Descriptiion
+     **/
+    public void sendMessage(final String message, final String exchangeName, final String exchangeType, final String queueName, final String routingKey, final SendMessageListener sendMessageListener,final ErrorMessageListener errorMessageListener) {
+        executor.execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    rabbitMQ.sendExchangeNameQueueMessage(exchangeName, exchangeType, message, queueName, routingKey);
+                    if (sendMessageListener != null) sendMessageListener.sendMessage(true);
+                } catch (IOException | TimeoutException | AlreadyClosedException e) {
+                    e.printStackTrace();
+                    if (errorMessageListener!=null){
+                        errorMessageListener.errorMessage(e);
+                    }
+                    if (sendMessageListener != null) sendMessageListener.sendMessage(false);
+                }
+            }
+        });
+    }
+
+    /**
+     * @param exchangeName 交换机名称
+     * @param queueName    队列名称
+     * @param message      需要发送的消息
+     * @date 创建时间:2020/9/8 0008
+     * @auther gaoxiaoxiong
+     * @Descriptiion
+     **/
+    public void sendFanoutTypeMessage(final String exchangeName, final String message, final String queueName, final SendMessageListener sendMessageListener,final ErrorMessageListener errorMessageListener) {
+        executor.execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    rabbitMQ.sendFanoutTypeMessage(exchangeName, queueName, message);
+                    if (sendMessageListener != null) sendMessageListener.sendMessage(true);
+                } catch (IOException | TimeoutException | AlreadyClosedException e) {
+                    e.printStackTrace();
+                    if (errorMessageListener!=null){
+                        errorMessageListener.errorMessage(e);
+                    }
+                    if (sendMessageListener != null) sendMessageListener.sendMessage(false);
+                }
+            }
+        });
+    }
+
+    /**
+     * @param exchangeName 交换机名称
+     * @param message      需要发送的消息
+     * @param queueName    队列名称
+     * @param routingKey   路由规则
+     * @date 创建时间:2020/9/8 0008
+     * @auther gaoxiaoxiong
+     * @Descriptiion 发送 exchangeType direct 类型的信息
+     **/
+    public void sendDirectTypeMessage(final String exchangeName, final String queueName, final String message, final String routingKey, final SendMessageListener sendMessageListener,final ErrorMessageListener errorMessageListener) {
+        executor.execute(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    rabbitMQ.sendDirectTypeMessage(exchangeName, queueName, message, routingKey);
+                    if (sendMessageListener != null) sendMessageListener.sendMessage(true);
+                } catch (IOException | TimeoutException | AlreadyClosedException e) {
+                    e.printStackTrace();
+                    if (errorMessageListener!=null){
+                        errorMessageListener.errorMessage(e);
+                    }
+                    if (sendMessageListener != null) sendMessageListener.sendMessage(false);
+                }
+            }
+        });
+    }
+
+    /**
+     * @param queueName 队列名称
+     * @date 创建时间:2020/9/8 0008
+     * @auther gaoxiaoxiong
+     * @Descriptiion
+     **/
+    public void receiveQueueMessage(String queueName, final ReceiveMessageListener listener,final ErrorMessageListener errorMessageListener) {
+        String newQueueName = null;
+        if (TextUtils.isEmpty(queueName)){
+            newQueueName = createDefaultQueueName(queueName);
+        }else {
+            newQueueName = queueName;
+        }
+        final String finalNewQueueName = newQueueName;
+        executor.execute(() -> {
+            while (isRunning) {
+                try {
+                    rabbitMQ.receiveQueueMessage(finalNewQueueName, message -> {
+                        if (listener != null) listener.receiveMessage(message);
+                    });
+                } catch (IOException | TimeoutException | AlreadyClosedException e) {
+                    if (errorMessageListener!=null){
+                        errorMessageListener.errorMessage(e);
+                    }
+                    e.printStackTrace();
+                    SystemClock.sleep(5000);
+                }
+            }
+        });
+    }
+
+    public void receiveQueueRoutingKeyMessage(String queueName, final String routingKey, final String exchangeName, final String exchangeType, final ReceiveMessageListener listener,final ErrorMessageListener errorMessageListener) {
+        String newQueueName = null;
+        if (TextUtils.isEmpty(queueName)){
+            newQueueName = createDefaultQueueName(queueName);
+        }else {
+            newQueueName = queueName;
+        }
+        final String finalNewQueueName = newQueueName;
+        executor.execute(new Runnable() {
+            @Override
+            public void run() {
+                while (isRunning) {
+                    try {
+                        rabbitMQ.receiveQueueRoutingKeyMessage(finalNewQueueName, routingKey, exchangeName, exchangeType, new RabbitMQClient.ResponseListener() {
+                            @Override
+                            public void receive(String message) {
+                                if (listener != null) listener.receiveMessage(message);
+                            }
+
+                        });
+                    } catch (IOException | TimeoutException | AlreadyClosedException e) {
+                        if (errorMessageListener!=null){
+                            errorMessageListener.errorMessage(e);
+                        }
+                        e.printStackTrace();
+                        SystemClock.sleep(5000);  //等待五秒
+                    }
+                }
+            }
+        });
+    }
+
+    public String createDefaultQueueName(String routingKey) {
+        if (TextUtils.isEmpty(routingKey)){
+            routingKey = "";
+        }
+        return routingKey + "@" + UUID.randomUUID();
+    }
+
+    /**
+     * 建议:
+     * 在application中关闭或者在结束工作时关闭
+     */
+    public void close() {
+        isRunning = false;
+        executor.execute(new Runnable() {
+            @Override
+            public void run() {
+                rabbitMQ.close();
+                executor.shutdownNow();
+            }
+        });
+    }
+
+
+    public interface ReceiveMessageListener {
+        void receiveMessage(String message);
+    }
+
+    public interface SendMessageListener {
+        void sendMessage(boolean isSuccess);
+    }
+
+    public interface ErrorMessageListener{
+        void errorMessage(Exception e);
+    }
+}
+

+ 4 - 0
app/src/main/java/com/bjca/hp/acupuncture/common/SharedPreferencesConfigs.kt

@@ -0,0 +1,4 @@
+package com.bjca.hp.acupuncture.common
+
+const val SHARE_RISK_LOCATION = "share_risk_location"
+const val SHARE_RISK_PURE = "share_risk_pure"

+ 72 - 0
app/src/main/java/com/bjca/hp/acupuncture/model/DrugUsage.kt

@@ -0,0 +1,72 @@
+package com.bjca.hp.acupuncture.model
+
+import com.google.gson.annotations.SerializedName
+
+data class DrugUsage(
+
+    @field:SerializedName("total")
+    val total: Int,
+
+    @field:SerializedName("size")
+    val size: Int,
+
+    @field:SerializedName("nulls")
+    val nulls: Any,
+
+    @field:SerializedName("index")
+    val index: Int,
+
+    @field:SerializedName("items")
+    val items: List<ItemsItem5>
+)
+
+data class ItemsItem5(
+
+    @field:SerializedName("code")
+    val code: String,
+
+    @field:SerializedName("sysCodes")
+    val sysCodes: Any,
+
+    @field:SerializedName("seqNo")
+    val seqNo: Int,
+
+    @field:SerializedName("memo")
+    val memo: Any,
+
+    @field:SerializedName("global")
+    val global: Boolean,
+
+    @field:SerializedName("type")
+    val type: String,
+
+    @field:SerializedName("parentType")
+    val parentType: Any,
+
+    @field:SerializedName("parentCode")
+    val parentCode: Any,
+
+    @field:SerializedName("codeChain")
+    val codeChain: String,
+
+    @field:SerializedName("ifMaster")
+    val ifMaster: Boolean,
+
+    @field:SerializedName("extra")
+    val extra: Any,
+
+    @field:SerializedName("disabled")
+    val disabled: Boolean,
+
+    @field:SerializedName("lockKey")
+    val lockKey: String,
+
+    @field:SerializedName("id")
+    val id: Int,
+
+    @field:SerializedName("text")
+    val text: String,
+
+    @field:SerializedName("shortCode")
+    val shortCode: String
+)

+ 71 - 0
app/src/main/java/com/bjca/hp/acupuncture/model/ItemsItem.kt

@@ -0,0 +1,71 @@
+package com.bjca.hp.acupuncture.model
+
+
+data class ItemsItems(
+    val assessorName: String,
+    val storehouseCode: String,
+    val sysCode: String,
+    val ifStateExpense: Boolean,
+    val patientId: Int,
+    val usage: String,
+    val medicalAdvice: String,
+    val dosagePerTime: String,
+    val frequency: String,
+    val approveUserId: Int,
+    val itemName: String,
+    val price: Double,
+    val paymentId: Int,
+    val patientAge: String,
+    val cashierId: String,
+    val lockKey: String,
+    val id: Int,
+    val ifInjury: Boolean,
+    val paymentState: String,
+    val invoiceNo: String,
+    val barcode: String,
+    val originalPaymentId: String,
+    val diagnosis: String,
+    val drugRpType: String,
+    val originalInvoiceNo: String,
+    val examineReason: String,
+    val opNo: String,
+    val name: String,
+    val executorDeptCode: String,
+    val assessorCode: String,
+    val refundDate: String,
+    val deptCode: String,
+    val makeMethod: String,
+    val distributeState: String,
+    val rpFeeType: String,
+    val no: String,
+    val settleId: String,
+    val approveDate: String,
+    val originalNo: String,
+    val patientGender: String,
+    val appNo: String,
+    val settleDate: String,
+    val adviceNo: String,
+    val doctorName: String,
+    val rpSettleType: String,
+    val doctorId: Int,
+    val offsetState: String,
+    val disabled: Boolean,
+    val drugRestrict: String,
+    val rpClass: String,
+    val groupNo: String,
+    val cashierName: String,
+    val ifInsurance: Boolean,
+    val examineResult: String,
+    val patientName: String,
+    val dosageUnit: String,
+    val cost: Int,
+    val quantity: String,
+    val ifDivide: String,
+    val excessDrugExplain: String,
+    val approveUserName: String,
+    val rpStatisticType: String,
+    val days: String,
+    val cashierCode: String,
+    val paymentDate: String,
+    val quantityPerDay: String
+)

+ 15 - 0
app/src/main/java/com/bjca/hp/acupuncture/model/MqMessage.kt

@@ -0,0 +1,15 @@
+package com.bjca.hp.acupuncture.model
+
+/***************************************************************************
+ * <pre></pre>
+ * @文件名称:  MqMessage
+ * @包   路   径:  com.bjca.hp.acupuncture.model
+ * @版权所有:北京数字医信责任有限公司 (C) 2022
+ *
+ * @类描述:
+ * @版本: V1.0
+ * @创建人: xuqm
+ * @创建时间:2022/6/20 下午 05:06
+ * @修改记录:
+ */
+data class MqMessage(val no:String)

+ 168 - 0
app/src/main/java/com/bjca/hp/acupuncture/model/RegModel.kt

@@ -0,0 +1,168 @@
+package com.bjca.hp.acupuncture.model
+
+import com.google.gson.annotations.SerializedName
+
+data class RegModel(
+
+    @field:SerializedName("total")
+    val total: Int,
+
+    @field:SerializedName("size")
+    val size: Int,
+
+    @field:SerializedName("nulls")
+    val nulls: String,
+
+    @field:SerializedName("index")
+    val index: Int,
+
+    @field:SerializedName("items")
+    val items: List<ItemsItem3>
+)
+
+data class ItemsItem3(
+
+    @field:SerializedName("date")
+    val date: String,
+
+    @field:SerializedName("additionUserId")
+    val additionUserId: String,
+
+    @field:SerializedName("deptName")
+    val deptName: String,
+
+    @field:SerializedName("seqNo")
+    val seqNo: Int,
+
+    @field:SerializedName("patientId")
+    val patientId: Int,
+
+    @field:SerializedName("signInDate")
+    val signInDate: String,
+
+    @field:SerializedName("patientGender")
+    val patientGender: String,
+
+    @field:SerializedName("opType")
+    val opType: String,
+
+    @field:SerializedName("ifSubsequent")
+    val ifSubsequent: Boolean,
+
+    @field:SerializedName("registrarName")
+    val registrarName: String,
+
+    @field:SerializedName("ifDoctorAdd")
+    val ifDoctorAdd: Boolean,
+
+    @field:SerializedName("cardNo")
+    val cardNo: String,
+
+    @field:SerializedName("additionUserName")
+    val additionUserName: String,
+
+    @field:SerializedName("treatDate")
+    val treatDate: String,
+
+    @field:SerializedName("takingDate")
+    val takingDate: String,
+
+    @field:SerializedName("ifDeleted")
+    val ifDeleted: Boolean,
+
+    @field:SerializedName("doctorName")
+    val doctorName: String,
+
+    @field:SerializedName("appointmentChannelCode")
+    val appointmentChannelCode: String,
+
+    @field:SerializedName("lineNo")
+    val lineNo: Double,
+
+    @field:SerializedName("doctorId")
+    val doctorId: Int,
+
+    @field:SerializedName("price")
+    val price: Double,
+
+    @field:SerializedName("ifAddition")
+    val ifAddition: Boolean,
+
+    @field:SerializedName("patientAge")
+    val patientAge: String,
+
+    @field:SerializedName("lockKey")
+    val lockKey: String,
+
+    @field:SerializedName("id")
+    val id: Int,
+
+    @field:SerializedName("state")
+    val state: String,
+
+    @field:SerializedName("paymentState")
+    val paymentState: String,
+
+    @field:SerializedName("regType")
+    val regType: String,
+
+    @field:SerializedName("periodEnd")
+    val periodEnd: String,
+
+    @field:SerializedName("registrarId")
+    val registrarId: Int,
+
+    @field:SerializedName("patientName")
+    val patientName: String,
+
+    @field:SerializedName("targetId")
+    val targetId: String,
+
+    @field:SerializedName("cardType")
+    val cardType: String,
+
+    @field:SerializedName("pay")
+    val pay: Double,
+
+    @field:SerializedName("lineId")
+    val lineId: Int,
+
+    @field:SerializedName("occupationalInjury")
+    val occupationalInjury: String,
+
+    @field:SerializedName("version")
+    val version: Int,
+
+    @field:SerializedName("patientPhone")
+    val patientPhone: String,
+
+    @field:SerializedName("ifEmergency")
+    val ifEmergency: Boolean,
+
+    @field:SerializedName("patientLevel")
+    val patientLevel: String,
+
+    @field:SerializedName("periodType")
+    val periodType: String,
+
+    @field:SerializedName("opNo")
+    val opNo: String,
+
+    @field:SerializedName("ifMaternityInsurance")
+    val ifMaternityInsurance: Boolean,
+
+    @field:SerializedName("periodStart")
+    val periodStart: String,
+
+    @field:SerializedName("appointmentDate")
+    val appointmentDate: String,
+
+    @field:SerializedName("appCardType")
+    val appCardType: String,
+
+    @field:SerializedName("deptCode")
+    val deptCode: String,
+
+    @field:SerializedName("appCardNo")
+    val appCardNo: String
+)

+ 153 - 0
app/src/main/java/com/bjca/hp/acupuncture/model/RpDetailModel.kt

@@ -0,0 +1,153 @@
+package com.bjca.hp.acupuncture.model
+
+import com.google.gson.annotations.SerializedName
+
+data class RpDetailModel(
+
+    @field:SerializedName("total")
+    val total: Int,
+
+    @field:SerializedName("size")
+    val size: Int,
+
+    @field:SerializedName("nulls")
+    val nulls: String,
+
+    @field:SerializedName("index")
+    val index: Int,
+
+    @field:SerializedName("items")
+    val items: List<ItemsItem2>
+)
+
+data class ItemsItem2(
+
+    @field:SerializedName("shelfIndex")
+    val shelfIndex: String,
+
+    @field:SerializedName("shelfCommand")
+    val shelfCommand: String,
+
+    @field:SerializedName("standard")
+    val standard: String,
+
+    @field:SerializedName("no")
+    val no: String,
+
+    @field:SerializedName("storehouseCode")
+    val storehouseCode: String,
+
+    @field:SerializedName("code")
+    val code: String,
+
+    @field:SerializedName("usage")
+    val usage: String,
+
+    @field:SerializedName("appNo")
+    val appNo: String,
+
+    @field:SerializedName("memo")
+    val memo: String,
+
+    @field:SerializedName("herbalSpecialUsage")
+    val herbalSpecialUsage: String,
+
+    @field:SerializedName("restrict")
+    val restrict: String,
+
+    @field:SerializedName("type")
+    val type: String,
+
+    @field:SerializedName("dosagePerTime")
+    val dosagePerTime: String,
+
+    @field:SerializedName("frequency")
+    val frequency: String,
+
+    @field:SerializedName("insuranceCode")
+    val insuranceCode: String,
+
+    @field:SerializedName("totalDose")
+    val totalDose: String,
+
+    @field:SerializedName("times")
+    val times: String,
+
+    @field:SerializedName("price")
+    val price: Double,
+
+    @field:SerializedName("mnemonic")
+    val mnemonic: String,
+
+    @field:SerializedName("offsetState")
+    val offsetState: String,
+
+    @field:SerializedName("warning")
+    val warning: String,
+
+    @field:SerializedName("lockKey")
+    val lockKey: String,
+
+    @field:SerializedName("id")
+    val id: Int,
+
+    @field:SerializedName("rpClass")
+    val rpClass: String,
+
+    @field:SerializedName("groupNo")
+    val groupNo: String,
+
+    @field:SerializedName("key")
+    val key: String,
+
+    @field:SerializedName("dosageUnit")
+    val dosageUnit: String,
+
+    @field:SerializedName("quantity")
+    val quantity: Int,
+
+    @field:SerializedName("cost")
+    val cost: Double,
+
+    @field:SerializedName("rpNo")
+    val rpNo: String,
+
+    @field:SerializedName("lotNumber")
+    val lotNumber: String,
+
+    @field:SerializedName("manufacturerCode")
+    val manufacturerCode: String,
+
+    @field:SerializedName("approvalNumber")
+    val approvalNumber: String,
+
+    @field:SerializedName("feeType")
+    val feeType: String,
+
+    @field:SerializedName("unit")
+    val unit: String,
+
+    @field:SerializedName("symptom")
+    val symptom: String,
+
+    @field:SerializedName("form")
+    val form: String,
+
+    @field:SerializedName("opNo")
+    val opNo: String,
+
+    @field:SerializedName("name")
+    val name: String,
+
+    @field:SerializedName("parts")
+    val parts: String,
+
+    @field:SerializedName("days")
+    val days: Int,
+
+    @field:SerializedName("executorDeptCode")
+    val executorDeptCode: String,
+
+    @field:SerializedName("offsetId")
+    val offsetId: Any
+)

+ 21 - 0
app/src/main/java/com/bjca/hp/acupuncture/model/RpModel.kt

@@ -0,0 +1,21 @@
+package com.bjca.hp.acupuncture.model
+
+import com.google.gson.annotations.SerializedName
+
+data class RpModel(
+
+    @field:SerializedName("total")
+    val total: Int,
+
+    @field:SerializedName("size")
+    val size: Int,
+
+    @field:SerializedName("nulls")
+    val nulls: Any,
+
+    @field:SerializedName("index")
+    val index: Int,
+
+    @field:SerializedName("items")
+    val items: List<ItemsItems>
+)

+ 113 - 0
app/src/main/java/com/bjca/hp/acupuncture/model/WelcomeLIstModel.kt

@@ -0,0 +1,113 @@
+package com.bjca.hp.acupuncture.model
+
+import com.google.gson.annotations.SerializedName
+import com.xuqm.base.adapter.BaseItem
+import java.util.ArrayList
+
+data class WelcomeLIstModel(
+
+    @field:SerializedName("total")
+    val total: Int? = null,
+
+    @field:SerializedName("size")
+    val size: Int? = null,
+
+    @field:SerializedName("nulls")
+    val nulls: Any? = null,
+
+    @field:SerializedName("index")
+    val index: Int? = null,
+
+    @field:SerializedName("items")
+    val items: ArrayList<ItemsItem> = arrayListOf()
+)
+
+data class ItemsItem(
+
+    @field:SerializedName("standard")
+    val standard: String? = null,
+
+    @field:SerializedName("storehouseCode")
+    val storehouseCode: String? = null,
+
+    @field:SerializedName("shelfIndex")
+    val shelfIndex: Any? = null,
+
+    @field:SerializedName("code")
+    val code: String? = null,
+
+    @field:SerializedName("purpose")
+    val purpose: String? = null,
+
+    @field:SerializedName("supplierCode")
+    val supplierCode: String? = null,
+
+    @field:SerializedName("herbalSku")
+    val herbalSku: String? = null,
+
+    @field:SerializedName("type")
+    val type: String? = null,
+
+    @field:SerializedName("restrict")
+    val restrict: String? = null,
+
+    @field:SerializedName("rate")
+    val rate: Double? = null,
+
+    @field:SerializedName("price")
+    val price: Double? = null,
+
+    @field:SerializedName("srcId")
+    val srcId: Any? = null,
+
+    @field:SerializedName("disabled")
+    val disabled: Boolean? = null,
+
+    @field:SerializedName("lockKey")
+    val lockKey: Any? = null,
+
+    @field:SerializedName("id")
+    val id: Int? = null,
+
+    @field:SerializedName("applyQuantity")
+    val applyQuantity: Double? = null,
+
+    @field:SerializedName("shortCode")
+    val shortCode: String? = null,
+
+    @field:SerializedName("expirationDate")
+    val expirationDate: String? = null,
+
+    @field:SerializedName("brandName")
+    val brandName: String? = null,
+
+    @field:SerializedName("cost")
+    val cost: Double? = null,
+
+    @field:SerializedName("quantity")
+    val quantity: Double? = null,
+
+    @field:SerializedName("lotNumber")
+    val lotNumber: String? = null,
+
+    @field:SerializedName("manufacturerCode")
+    val manufacturerCode: String? = null,
+
+    @field:SerializedName("feeType")
+    val feeType: Any? = null,
+
+    @field:SerializedName("approvalNumber")
+    val approvalNumber: Any? = null,
+
+    @field:SerializedName("ceilType")
+    val ceilType: String? = null,
+
+    @field:SerializedName("unit")
+    val unit: String? = null,
+
+    @field:SerializedName("form")
+    val form: String? = null,
+
+    @field:SerializedName("name")
+    val name: String? = null
+) : BaseItem()

+ 30 - 0
app/src/main/java/com/bjca/hp/acupuncture/receiver/BootCompleteReceiver.kt

@@ -0,0 +1,30 @@
+package com.bjca.hp.acupuncture.receiver
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import com.bjca.hp.acupuncture.ui.MainActivity
+
+/***************************************************************************
+ * <pre></pre>
+ * @文件名称:  BootCompleteReceiver
+ * @包   路   径:  com.bjca.hp.acupuncture.receiver
+ * @版权所有:北京数字医信责任有限公司 (C) 2022
+ *
+ * @类描述:
+ * @版本: V1.0
+ * @创建人: xuqm
+ * @创建时间:2022/6/24 下午 06:34
+ * @修改记录:
+ */
+class BootCompleteReceiver : BroadcastReceiver(){
+    override fun onReceive(context: Context, intent: Intent) {
+        if(Intent.ACTION_BOOT_COMPLETED == intent.action){
+            val thisIntent = Intent(context, MainActivity::class.java)
+            thisIntent.action = "android.intent.action.MAIN";
+            thisIntent.addCategory("android.intent.category.LAUNCHER");
+            thisIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK;
+            context.startActivity(thisIntent);
+        }
+    }
+}

+ 130 - 0
app/src/main/java/com/bjca/hp/acupuncture/repository/HeaderInterceptor.kt

@@ -0,0 +1,130 @@
+package com.bjca.hp.acupuncture.repository
+
+import android.content.Context
+import com.xuqm.base.common.SHARE_UESR_TOKEN
+import com.xuqm.base.extensions.getStringForPreferences
+import com.xuqm.base.extensions.log
+import com.xuqm.base.extensions.loge
+import okhttp3.Headers
+import okhttp3.Interceptor
+import okhttp3.Response
+import okhttp3.internal.http.HttpHeaders
+import okhttp3.internal.http.StatusLine
+import okio.Buffer
+import okio.BufferedSource
+import okio.GzipSource
+import java.io.EOFException
+import java.net.HttpURLConnection
+import java.nio.charset.Charset
+
+
+class HeaderInterceptor(val context: Context) : Interceptor {
+    val UTF8 = Charset.forName("UTF-8")
+    override fun intercept(chain: Interceptor.Chain): Response {
+        val original = chain.request()
+
+        //请求定制:添加请求头
+        val requestBuilder = original.newBuilder()
+            .header("Authentication", context.getStringForPreferences(SHARE_UESR_TOKEN))
+            .addHeader("Content-Type", "application/json;charset=UTF-8")
+
+//        context.getStringForPreferences(SHARE_UESR_TOKEN).loge()
+
+        val request = requestBuilder.build()
+        "${request.url()}(${request.method()})".loge()
+
+        val headers = request.headers()
+
+//        request.body()?.also {
+//
+//            if (!bodyHasUnknownEncoding(headers)) {
+//                val buffer = Buffer()
+//                it.writeTo(buffer)
+//
+//                var charset = Charset.forName("UTF-8")
+//                it.contentType()?.also { its -> charset = its.charset(Charset.forName("UTF-8"))!! }
+//
+////                if (isPlaintext(buffer)) {
+////                    buffer.readString(charset).loge()
+////                }
+//            }
+//        }
+        val response = chain.proceed(request)
+
+        response.body()?.also {
+            if (!bodyHasUnknownEncoding(headers) && hasBody(response)) {
+                val source: BufferedSource = it.source()
+                source.request(Long.MAX_VALUE) // Buffer the entire body.
+                var buffer = source.buffer
+                if ("gzip".equals(headers.get("Content-Encoding"), ignoreCase = true)) {
+                    var gzippedResponseBody: GzipSource? = null
+                    try {
+                        gzippedResponseBody = GzipSource(buffer.clone())
+                        buffer = Buffer()
+                        buffer.writeAll(gzippedResponseBody)
+                    } finally {
+                        gzippedResponseBody?.close()
+                    }
+                }
+                var charset = UTF8
+                it.contentType()?.also { its -> charset = its.charset(Charset.forName("UTF-8"))!! }
+
+
+                if (isPlaintext(buffer)) {
+                    buffer.clone().readString(charset).log()
+                }
+            }
+        }
+
+
+
+
+        return response
+    }
+
+    private fun bodyHasUnknownEncoding(headers: Headers): Boolean {
+        val contentEncoding = headers["Content-Encoding"]
+        return (contentEncoding != null && !contentEncoding.equals("identity", ignoreCase = true)
+                && !contentEncoding.equals("gzip", ignoreCase = true))
+    }
+
+    private fun isPlaintext(buffer: Buffer): Boolean {
+        return try {
+            val prefix = Buffer()
+            val byteCount = if (buffer.size < 64) buffer.size else 64
+            buffer.copyTo(prefix, 0, byteCount)
+            for (i in 0..15) {
+                if (prefix.exhausted()) {
+                    break
+                }
+                val codePoint = prefix.readUtf8CodePoint()
+                if (Character.isISOControl(codePoint) && !Character.isWhitespace(codePoint)) {
+                    return false
+                }
+            }
+            true
+        } catch (e: EOFException) {
+            false // Truncated UTF-8 sequence.
+        }
+    }
+
+    fun hasBody(response: Response): Boolean {
+        // HEAD requests never yield a body regardless of the response headers.
+        if (response.request().method() == "HEAD") {
+            return false
+        }
+        val responseCode = response.code()
+        if ((responseCode < StatusLine.HTTP_CONTINUE || responseCode >= 200)
+            && responseCode != HttpURLConnection.HTTP_NO_CONTENT && responseCode != HttpURLConnection.HTTP_NOT_MODIFIED
+        ) {
+            return true
+        }
+
+        // If the Content-Length or Transfer-Encoding headers disagree with the response code, the
+        // response is malformed. For best compatibility, we honor the headers.
+        return HttpHeaders.contentLength(response) != -1L || "chunked".equals(
+            response.header("Transfer-Encoding"),
+            ignoreCase = true
+        )
+    }
+}

+ 30 - 0
app/src/main/java/com/bjca/hp/acupuncture/repository/Service.kt

@@ -0,0 +1,30 @@
+package com.bjca.hp.acupuncture.repository
+
+import com.bjca.hp.acupuncture.model.*
+import com.xuqm.sdhbwfu.core.model.HttpResult
+import io.reactivex.Observable
+import retrofit2.http.GET
+import retrofit2.http.Path
+import retrofit2.http.Query
+
+
+interface Service {
+    @GET("drug/stock/standard?storehouseCode=2&type=&form=&purpose=&restrict=&danger=&antibiotic=&keyword=&manufacturerCode=&supplierCode=&expirationDateMin=&expirationDateMax=&sort=id&asc=false&papeIndexOnView=2&pageSize=20&tenantId=101")
+    fun standard(@Query("pageIndex") pageIndex: Int): Observable<WelcomeLIstModel>
+
+    // op/rp?no=CF220609004247&rpClass=药品&tenantId=101
+    @GET("op/rp?rpClass=药品&tenantId=101")
+    fun rp(@Query("no") no: String): Observable<RpModel>
+
+    // op/rpDetail?opNo=MZ220321036030&rpNo=CF220323001947&tenantId=101
+    @GET("op/rpDetail?tenantId=101")
+    fun rpDetail(@Query("opNo") opNo: String, @Query("rpNo") rpNo: String): Observable<RpDetailModel>
+
+    // op/reg?opNo=MZ220321036030&tenantId=101
+    @GET("op/reg?tenantId=101")
+    fun reg(@Query("opNo") opNo: String): Observable<RegModel>
+
+    // dict/DrugUsage?sort=id&asc=false&pageIndexOnView=1&pageSize=100&dictType=DrugType&pageIndex=0
+    @GET("dict/DrugUsage?sort=id&asc=false&pageIndexOnView=1&pageSize=100&dictType=DrugType&pageIndex=0")
+    fun drugUsage(): Observable<DrugUsage>
+}

+ 243 - 0
app/src/main/java/com/bjca/hp/acupuncture/ui/MainActivity.kt

@@ -0,0 +1,243 @@
+package com.bjca.hp.acupuncture.ui
+
+import android.graphics.Typeface
+import android.os.Bundle
+import androidx.activity.viewModels
+import com.bigkoo.alertview.AlertView
+import com.bjca.hp.acupuncture.MyApplication
+import com.bjca.hp.acupuncture.R
+import com.bjca.hp.acupuncture.databinding.ActivityMainBinding
+import com.bjca.hp.acupuncture.model.ItemsItem2
+import com.bjca.hp.acupuncture.model.ItemsItem3
+import com.bjca.hp.acupuncture.model.ItemsItem5
+import com.bjca.hp.acupuncture.model.ItemsItems
+import com.bjca.hp.acupuncture.viewmodel.MainVM
+import com.xuqm.base.adapter.CommonAdapter
+import com.xuqm.base.adapter.ViewHolder
+import com.xuqm.base.dialog.loading.LoadingDialog
+import com.xuqm.base.extensions.loge
+import com.xuqm.base.ui.BaseActivity
+import com.xuqm.sdhbwfu.core.extensions.linearLayoutManager
+import com.xuqm.sdhbwfu.core.extensions.showMessage
+import tp.xmaihh.serialport.SerialHelper
+import tp.xmaihh.serialport.bean.ComBean
+
+class MainActivity : BaseActivity<ActivityMainBinding>() {
+    override fun fullscreen(): Boolean = true
+    override fun getLayoutId(): Int = R.layout.activity_main
+
+    private val vm: MainVM by viewModels()
+
+    override fun initView(savedInstanceState: Bundle?) {
+        super.initView(savedInstanceState)
+
+
+        sourceHanSansCNBold =
+            Typeface.createFromAsset(mContext?.assets, "fonts/SourceHanSansCN-Bold.ttf")
+        sourceHanSansCNMedium =
+            Typeface.createFromAsset(mContext?.assets, "fonts/SourceHanSansCN-Medium.ttf")
+        sourceHanSansCNNormal =
+            Typeface.createFromAsset(mContext?.assets, "fonts/SourceHanSansCN-Normal.ttf")
+        sourceHanSansCNRegular =
+            Typeface.createFromAsset(mContext?.assets, "fonts/SourceHanSansCN-Regular.ttf")
+        din =
+            Typeface.createFromAsset(mContext?.assets, "fonts/DIN-Alternate-Bold.ttf")
+
+        binding.title.typeface = sourceHanSansCNBold
+        binding.name.typeface = sourceHanSansCNBold
+        binding.tags.typeface = sourceHanSansCNMedium
+        binding.btn.typeface = sourceHanSansCNBold
+
+        binding.title1.typeface = sourceHanSansCNMedium
+        binding.title2.typeface = sourceHanSansCNMedium
+        binding.title3.typeface = sourceHanSansCNMedium
+        binding.title5.typeface = sourceHanSansCNMedium
+
+        binding.n1.typeface = sourceHanSansCNRegular
+        binding.v1.typeface = sourceHanSansCNRegular
+        binding.n2.typeface = sourceHanSansCNRegular
+        binding.v2.typeface = sourceHanSansCNRegular
+        binding.n3.typeface = sourceHanSansCNRegular
+        binding.v3.typeface = sourceHanSansCNRegular
+        binding.n4.typeface = sourceHanSansCNRegular
+        binding.v4.typeface = sourceHanSansCNRegular
+        binding.n5.typeface = sourceHanSansCNRegular
+        binding.v5.typeface = sourceHanSansCNRegular
+        binding.n6.typeface = sourceHanSansCNRegular
+        binding.v6.typeface = sourceHanSansCNRegular
+        binding.n7.typeface = sourceHanSansCNRegular
+        binding.v7.typeface = sourceHanSansCNRegular
+        binding.n8.typeface = sourceHanSansCNRegular
+        binding.v8.typeface = sourceHanSansCNRegular
+        binding.n9.typeface = sourceHanSansCNRegular
+        binding.v9.typeface = sourceHanSansCNRegular
+        binding.n10.typeface = sourceHanSansCNRegular
+        binding.v10.typeface = sourceHanSansCNRegular
+        binding.n11.typeface = sourceHanSansCNRegular
+        binding.v11.typeface = sourceHanSansCNRegular
+        binding.n12.typeface = sourceHanSansCNRegular
+        binding.v12.typeface = sourceHanSansCNRegular
+        binding.n13.typeface = sourceHanSansCNRegular
+        binding.v13.typeface = sourceHanSansCNRegular
+        binding.n14.typeface = sourceHanSansCNRegular
+        binding.v14.typeface = sourceHanSansCNRegular
+        binding.n15.typeface = sourceHanSansCNRegular
+        binding.v15.typeface = sourceHanSansCNRegular
+        binding.n16.typeface = sourceHanSansCNRegular
+        binding.v16.typeface = sourceHanSansCNRegular
+        binding.n17.typeface = sourceHanSansCNRegular
+        binding.v17.typeface = sourceHanSansCNRegular
+
+
+        binding.list.linearLayoutManager(mContext, 15)
+        binding.list.adapter = adapter
+
+
+        binding.btn.setOnClickListener {
+            LoadingDialog.showDialog("")
+
+            if (adapter.datas.size > 0)
+                sndMessage(0)
+            else {
+//                s.sendHex("0105002cff004DF3");  // 发送Hex
+
+                MyApplication.getInstance().runOnUiThreadDelay({
+                    try {
+                        s.sendHex("02050001ff00DDC9");  // 发送Hex
+                    } catch (e: Exception) {
+                        e.showMessage()
+                    }
+                    LoadingDialog.dismissDialog()
+                }, 500)
+            }
+        }
+
+    }
+
+    private fun sndMessage(i: Int) {
+        if (i >= adapter.datas.size) {
+            LoadingDialog.dismissDialog()
+            AlertView(
+                "取药完成", adapter.datas.map { it.shelfIndex}.joinToString(), null, arrayOf("确定"), null, mContext,
+                AlertView.Style.Alert
+            ) { _, _ ->
+                mContext.finish()
+            }.show()
+            return
+        }
+        try {
+            s.sendHex(adapter.datas[i].shelfCommand);  // 发送Hex
+        } catch (e: Exception) {
+            e.showMessage()
+        }
+        MyApplication.getInstance().runOnUiThreadDelay({
+            sndMessage(i + 1)
+        }, 2300)
+
+    }
+
+    lateinit var sourceHanSansCNBold: Typeface
+    lateinit var sourceHanSansCNMedium: Typeface
+    lateinit var sourceHanSansCNNormal: Typeface
+    lateinit var sourceHanSansCNRegular: Typeface
+    lateinit var din: Typeface
+
+    var rp: ItemsItems? = null
+    var regM: ItemsItem3? = null
+    var usages: List<ItemsItem5> = listOf()
+    lateinit var s: SerialHelper
+    override fun initData() {
+        super.initData()
+        s = object : SerialHelper("/dev/ttyS0", 38400) {
+            override fun onDataReceived(paramComBean: ComBean?) {
+                paramComBean?.bRec?.loge()
+            }
+        }
+        try {
+            s.open()
+        } catch (e: Exception) {
+            e.showMessage()
+        }
+
+        vm.status.observe(this) {
+            rp = it
+            rerresh()
+        }
+        vm.regM.observe(this) {
+            regM = it
+            rerresh()
+        }
+        vm.usages.observe(this) {
+            usages = it ?: listOf()
+            intent.getStringExtra("opNo")?.let { it1 -> vm.getRp(it1) }
+        }
+        vm.rpDetail.observe(this) {
+            adapter.setmDatas(it)
+        }
+        vm.drugUsage()
+    }
+
+    fun getUsage(code: String): String {
+        var text = "--"
+        usages.forEach {
+            if (it.code == code) {
+                text = it.text
+            }
+        }
+        return text
+    }
+
+    private fun rerresh() {
+        rp?.let {
+            binding.v8.text = it.diagnosis
+            binding.v14.text = it.doctorName
+            binding.v16.text = "${it.price}元"
+            binding.v13.text = it.approveUserName
+            binding.v11.text = it.no
+        }
+        regM?.let {
+            binding.v1.text = it.patientName
+            binding.v2.text = it.patientGender
+            binding.v3.text = "${it.patientAge}"
+            binding.v4.text = it.patientPhone
+            binding.v5.text = ""
+
+            binding.v6.text = it.appCardNo
+            binding.v7.text = it.deptName
+            binding.v9.text = it.date
+            binding.v10.text = it.opType
+
+        }
+    }
+
+
+    private val adapter = object : CommonAdapter<ItemsItem2>(R.layout.item_main) {
+        override fun convert(holder: ViewHolder, item: ItemsItem2, position: Int) {
+
+            holder.setTypeface(R.id.index, sourceHanSansCNMedium)
+                .setText(R.id.index, "${position + 1}")
+                .setTypeface(R.id.n1, sourceHanSansCNRegular)
+                .setTypeface(R.id.v1, sourceHanSansCNRegular)
+                .setText(R.id.v1, item.name)//名称
+                .setTypeface(R.id.n2, sourceHanSansCNRegular)
+                .setTypeface(R.id.v1, sourceHanSansCNRegular)
+                .setText(R.id.v2, item.standard)//规格
+                .setTypeface(R.id.n3, sourceHanSansCNRegular)
+                .setTypeface(R.id.v3, sourceHanSansCNRegular)
+                .setText(R.id.v3, "${item.quantity}${item.unit}")//数量
+                .setTypeface(R.id.n4, sourceHanSansCNRegular)
+                .setTypeface(R.id.v4, sourceHanSansCNRegular)
+                .setText(R.id.v4, getUsage(item.usage))//使用方法---码表
+                .setTypeface(R.id.n5, sourceHanSansCNRegular)
+                .setTypeface(R.id.v5, sourceHanSansCNRegular)
+                .setText(R.id.v5, "${item.dosagePerTime}${item.dosageUnit}")//用量
+                .setTypeface(R.id.n6, sourceHanSansCNRegular)
+                .setTypeface(R.id.v6, sourceHanSansCNRegular)
+                .setText(R.id.v6, item.frequency)//频次---码表
+                .setTypeface(R.id.n7, sourceHanSansCNRegular)
+                .setTypeface(R.id.v7, sourceHanSansCNRegular)
+                .setText(R.id.v7, "${item.days}天")//使用周期
+
+        }
+    }
+}

+ 209 - 0
app/src/main/java/com/bjca/hp/acupuncture/ui/SettingActivity.kt

@@ -0,0 +1,209 @@
+package com.bjca.hp.acupuncture.ui
+
+import android.os.Bundle
+import com.bjca.hp.acupuncture.MyApplication
+import com.bjca.hp.acupuncture.R
+import com.bjca.hp.acupuncture.databinding.ActivitySettingBinding
+import com.xuqm.base.extensions.loge
+import com.xuqm.base.ui.BaseActivity
+import com.xuqm.sdhbwfu.core.extensions.showMessage
+import kotlinx.android.synthetic.main.activity_setting.*
+import tp.xmaihh.serialport.SerialHelper
+import tp.xmaihh.serialport.bean.ComBean
+import java.util.*
+
+class SettingActivity : BaseActivity<ActivitySettingBinding>() {
+    override fun getLayoutId(): Int = R.layout.activity_setting
+    override fun initView(savedInstanceState: Bundle?) {
+        super.initView(savedInstanceState)
+        binding.a13.setOnClickListener {
+
+            val a11 = binding.a11.text.toString().toInt()
+            if (a11 <= 0 || a11 > 9) {
+                "请输入正确的柜号,从1开始".showMessage()
+                return@setOnClickListener
+            }
+            val a12 = binding.a12.text.toString().toInt()
+            if (a12 <= 0) {
+                "请输入正确的柜门号,从1开始".showMessage()
+                return@setOnClickListener
+            }
+            val b1 = "0${a11}05${intToHex(a12, 4)}ff00"
+            val b2 = getCRC(b1)
+            try {
+                s.sendHex("${b1}${b2}");  // 发送Hex
+            } catch (e: Exception) {
+                e.showMessage()
+            }
+
+        }
+        binding.a24.setOnClickListener {
+
+            val a21 = binding.a21.text.toString().toInt()
+            if (a21 <= 0 || a21 > 9) {
+                "请输入正确的柜号,从1开始".showMessage()
+                return@setOnClickListener
+            }
+            val a22 = binding.a22.text.toString().toInt()
+            if (a22 <= 0) {
+                "请输入正确的柜门号,从1开始".showMessage()
+                return@setOnClickListener
+            }
+            val a23 = binding.a23.text.toString().toInt()
+            if (a23 <= 0) {
+                "请输入正确的柜门号,从1开始".showMessage()
+                return@setOnClickListener
+            }
+
+            sndMessage(a21, a22, a23)
+
+        }
+        binding.a31.setOnClickListener {
+            sndMessage(1, 1, 84)
+        }
+        binding.a32.setOnClickListener {
+            sndMessage(2, 1, 84)
+        }
+        binding.a33.setOnClickListener {
+            sndMessage(3, 1, 84)
+        }
+    }
+
+
+    private fun sndMessage(i: Int, ii: Int, iii: Int) {
+        if (ii > iii) return
+        val b1 = "0${i}05${intToHex(ii, 4)}ff00"
+        val b2 = getCRC(b1)
+        try {
+            s.sendHex("${b1}${b2}");  // 发送Hex
+        } catch (e: Exception) {
+            e.showMessage()
+        }
+        MyApplication.getInstance().runOnUiThreadDelay({
+            sndMessage(i, ii + 1, iii)
+        }, 2300)
+
+    }
+
+
+    lateinit var s: SerialHelper
+    override fun initData() {
+        super.initData()
+        s = object : SerialHelper("/dev/ttyS0", 38400) {
+            override fun onDataReceived(paramComBean: ComBean?) {
+                paramComBean?.bRec?.loge()
+            }
+        }
+        try {
+            s.open()
+        } catch (e: Exception) {
+            e.showMessage()
+        }
+    }
+
+    /**
+     * 10进制转16进制.
+     *
+     * @param n    10进制数
+     * @param size 转换后的16进制位数
+     * @return 转换结果
+     */
+    private fun intToHex(n: Int, size: Int): String {
+        var n = n
+        var s = StringBuffer()
+        var a: String
+        val b = charArrayOf(
+            '0',
+            '1',
+            '2',
+            '3',
+            '4',
+            '5',
+            '6',
+            '7',
+            '8',
+            '9',
+            'A',
+            'B',
+            'C',
+            'D',
+            'E',
+            'F'
+        )
+        while (n != 0) {
+            s = s.append(b[n % 16])
+            n = n / 16
+        }
+        a = s.reverse().toString()
+        a = add_zero(a, size)
+        return a
+    }
+
+    fun add_zero(str: String, size: Int): String {
+        var str = str
+        if (str.length < size) {
+            str = "0$str"
+            str = add_zero(str, size)
+        }
+        return str
+    }
+
+
+    fun getCRC(data: String): String {
+        var data = data
+        data = data.replace(" ", "")
+        val len = data.length
+        if (len % 2 != 0) {
+            return "0000"
+        }
+        val num = len / 2
+        val para = ByteArray(num)
+        for (i in 0 until num) {
+            val value = Integer.valueOf(data.substring(i * 2, 2 * (i + 1)), 16)
+            para[i] = value.toByte()
+        }
+        return getCRC(para)
+    }
+
+    /**
+     * 计算CRC16校验码
+     *
+     * @param bytes
+     * 字节数组
+     * @return [String] 校验码
+     * @since 1.0
+     */
+    fun getCRC(bytes: ByteArray): String {
+        // CRC寄存器全为1
+        var CRC = 0x0000ffff
+        // 多项式校验值
+        val POLYNOMIAL = 0x0000a001
+        var i: Int
+        var j: Int
+        i = 0
+        while (i < bytes.size) {
+            CRC = CRC xor (bytes[i].toInt() and 0x000000ff)
+            j = 0
+            while (j < 8) {
+                if (CRC and 0x00000001 != 0) {
+                    CRC = CRC shr 1
+                    CRC = CRC xor POLYNOMIAL
+                } else {
+                    CRC = CRC shr 1
+                }
+                j++
+            }
+            i++
+        }
+        // 结果转换为16进制
+        var result = Integer.toHexString(CRC).uppercase(Locale.getDefault())
+        if (result.length != 4) {
+            val sb = StringBuffer("0000")
+            result = sb.replace(4 - result.length, 4, result).toString()
+        }
+        //高位在前地位在后
+        //return result.substring(2, 4) + " " + result.substring(0, 2);
+        // 交换高低位,低位在前高位在后
+        return result.substring(2, 4) + result.substring(0, 2)
+    }
+}

+ 36 - 0
app/src/main/java/com/bjca/hp/acupuncture/ui/TestActivity.kt

@@ -0,0 +1,36 @@
+package com.bjca.hp.acupuncture.ui
+
+import android.os.Build.VERSION_CODES.R
+import android.os.Bundle
+import com.bjca.hp.acupuncture.R
+import com.bjca.hp.acupuncture.databinding.ActivityTestBinding
+import com.xuqm.base.common.ToolsHelper
+import com.xuqm.base.extensions.getStringForPreferences
+import com.xuqm.base.extensions.putString
+import com.xuqm.base.ui.BaseActivity
+import com.xuqm.base.web.XWebViewHelper
+
+class TestActivity : BaseActivity<ActivityTestBinding>() {
+    override fun getLayoutId(): Int = R.layout.activity_test
+    override fun initView(savedInstanceState: Bundle?) {
+        super.initView(savedInstanceState)
+
+        val str = getStringForPreferences("wurl")
+        if (!ToolsHelper.isNull(str))
+            binding.editUrl.setText(str)
+
+        binding.btn.setOnClickListener {
+
+            val url =
+                if (ToolsHelper.isNull(binding.editUrl.text)) "https://web.sdk.qcloud.com/trtc/webrtc/demo/quick-demo-vue3-ts/index.html#/"
+                else binding.editUrl.text.toString()
+
+            putString("wurl", url)
+            XWebViewHelper.startWebNoTopBar(
+                mContext,
+                url
+            )
+        }
+    }
+
+}

+ 166 - 0
app/src/main/java/com/bjca/hp/acupuncture/ui/WelcomeActivity.kt

@@ -0,0 +1,166 @@
+package com.bjca.hp.acupuncture.ui
+
+import android.content.Intent
+import android.graphics.Typeface
+import android.os.Bundle
+import com.bjca.hp.acupuncture.R
+import com.bjca.hp.acupuncture.common.RabbitMQUtil
+import com.bjca.hp.acupuncture.databinding.ActivityWelcomeBinding
+import com.bjca.hp.acupuncture.model.ItemsItem
+import com.bjca.hp.acupuncture.viewmodel.WelcomeVM
+import com.xuqm.base.adapter.BasePagedAdapter
+import com.xuqm.base.adapter.CommonPagedAdapter
+import com.xuqm.base.adapter.ViewHolder
+import com.xuqm.base.ui.BaseListFormLayoutActivity
+import com.xuqm.sdhbwfu.core.extensions.gridLayoutManager
+import com.xuqm.sdhbwfu.core.extensions.runWithPermission
+import com.xuqm.sdhbwfu.core.extensions.toStrings
+
+class WelcomeActivity : BaseListFormLayoutActivity<ItemsItem, WelcomeVM, ActivityWelcomeBinding>() {
+
+    override fun getLayoutId(): Int = R.layout.activity_welcome
+    override fun fullscreen(): Boolean = true
+
+//    lateinit var s: SerialHelper
+
+    lateinit var sourceHanSansCNBold: Typeface
+    lateinit var sourceHanSansCNMedium: Typeface
+    lateinit var sourceHanSansCNNormal: Typeface
+    lateinit var din: Typeface
+    lateinit var mq: RabbitMQUtil
+
+    private var oldTime = 0L
+    private var index = 0
+
+    override fun initView(savedInstanceState: Bundle?) {
+        super.initView(savedInstanceState)
+
+        sourceHanSansCNBold =
+            Typeface.createFromAsset(mContext?.assets, "fonts/SourceHanSansCN-Bold.ttf")
+        sourceHanSansCNMedium =
+            Typeface.createFromAsset(mContext?.assets, "fonts/SourceHanSansCN-Medium.ttf")
+        sourceHanSansCNNormal =
+            Typeface.createFromAsset(mContext?.assets, "fonts/SourceHanSansCN-Normal.ttf")
+        din =
+            Typeface.createFromAsset(mContext?.assets, "fonts/DIN-Alternate-Bold.ttf")
+
+
+        binding.title.typeface = sourceHanSansCNBold
+
+        binding.title.setOnClickListener {
+            val newTime = System.currentTimeMillis()
+            if (newTime - oldTime < 2500 && oldTime != 0L) {
+                if (index > 5) {
+                    startActivity(Intent(mContext, SettingActivity::class.java))
+                    index = 0
+                }
+                index++
+            } else {
+                oldTime = newTime
+                index = 0
+            }
+        }
+
+//        binding.list.gridLayoutManager(mContext, 6, 2)
+//        binding.list.adapter = adapter
+
+//        adapter.setmDatas(arrayListOf("空调", "水泵", "空气净化器", "阳台", "灯"))
+        recyclerView.gridLayoutManager(mContext, 21, 3)
+
+
+        adapter.setItemLongClickListener { _, _, _ ->
+            startActivity(Intent(mContext, MainActivity::class.java))
+            return@setItemLongClickListener true
+        }
+
+    }
+
+    override fun onDestroy() {
+        super.onDestroy()
+        mq.close()
+    }
+
+    override fun initData() {
+        super.initData()
+
+
+        runWithPermission(
+            "android.permission.MODIFY_AUDIO_SETTINGS",
+            "android.permission.RECORD_AUDIO",
+            "android.permission.CAMERA"
+        ) {
+            startActivity(Intent(mContext, TestActivity::class.java))
+        }
+
+//        XWebViewHelper.startWebNoTopBar(mContext,"https://mdtdemo.51trust.com/")
+//        mq = RabbitMQUtil("10.10.202.11", 5672, "guest", "guest")
+//
+//        mq.receiveQueueMessage("RpNoByMedicineCabinet",
+//            {
+//
+//                startActivity(Intent(mContext, MainActivity::class.java)
+//                    .apply {
+//                        putExtra(
+//                            "opNo",
+//                            GsonImplHelp.get().toObject(it, MqMessage::class.java).no
+//                        )
+//                    })
+//            }
+//        ) {
+//            it.loge()
+//        }
+
+//        s = object : SerialHelper("/dev/ttyS0", 38400) {
+//            override fun onDataReceived(paramComBean: ComBean?) {
+//                paramComBean?.bRec?.loge()
+//            }
+//
+//        }
+//        s.setStickPackageHelper {
+//            try {
+//                val available = it.available()
+//                if (available > 0) {
+//                    val buffer = ByteArray(available)
+//                    val size = it.read(buffer)
+//                    if (size > 0) {
+//                        return@setStickPackageHelper buffer;
+//                    }
+//                } else {
+//                    SystemClock.sleep(50);
+//                }
+//
+//            } catch (e: IOException) {
+//                e.printStackTrace();
+//            }
+//            return@setStickPackageHelper null
+//        }
+
+//        s.sendHex("02050001ff00DDC9");  // 发送Hex
+
+    }
+
+
+    private val adapter = object : CommonPagedAdapter<ItemsItem>(R.layout.item_welcome) {
+        override fun convert(holder: ViewHolder, item: ItemsItem, position: Int) {
+            holder
+                .setTypeface(R.id.index, sourceHanSansCNMedium)
+                .setTypeface(R.id.name, sourceHanSansCNBold)
+                .setTypeface(R.id.hint, sourceHanSansCNNormal)
+                .setTypeface(R.id.number, din)
+                .setTypeface(R.id.unit, sourceHanSansCNNormal)
+                .setTypeface(R.id.balance, sourceHanSansCNNormal)
+                .setText(R.id.index, "${position + 1}")
+                .setText(R.id.name, item.name)
+                .setText(R.id.hint, item.supplierCode)
+                .setText(R.id.number, item.price?.toStrings())
+                .setText(R.id.unit, "元/${item.unit}")
+                .setText(R.id.balance, "剩余:${item.quantity}${item.unit}")
+
+
+        }
+    }
+
+    override fun adapter(): BasePagedAdapter<ItemsItem> = adapter
+
+
+}

+ 104 - 0
app/src/main/java/com/bjca/hp/acupuncture/viewmodel/MainVM.kt

@@ -0,0 +1,104 @@
+package com.bjca.hp.acupuncture.viewmodel
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.bjca.hp.acupuncture.MyApplication
+import com.bjca.hp.acupuncture.model.ItemsItem2
+import com.bjca.hp.acupuncture.model.ItemsItem3
+import com.bjca.hp.acupuncture.model.ItemsItem5
+import com.bjca.hp.acupuncture.model.ItemsItems
+import com.bjca.hp.acupuncture.repository.Service
+import com.xuqm.base.di.manager.HttpManager
+import com.xuqm.base.extensions.showMessage
+import com.xuqm.sdhbwfu.core.viewModel.BaseViewModel
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.schedulers.Schedulers
+
+/***************************************************************************
+ * <pre></pre>
+ * @文件名称:  WelcomeVM
+ * @包   路   径:  com.bjca.hp.acupuncture.viewmodel
+ * @版权所有:北京数字医信责任有限公司 (C) 2022
+ *
+ * @类描述:
+ * @版本: V1.0
+ * @创建人: xuqm
+ * @创建时间:2022/6/17 下午 02:33
+ * @修改记录:
+ */
+class MainVM : BaseViewModel() {
+
+
+    private val _status = MutableLiveData<ItemsItems>()
+    val status: LiveData<ItemsItems> = _status
+
+    fun getRp(opNo:String = "CF220609004247" ) {
+        HttpManager.getApi(MyApplication.appComponent1, Service::class.java)
+            .rp(opNo)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+            .subscribe(
+                {
+                    _status.postValue(it.items[0])
+                    rpDetail(it.items[0].opNo, it.items[0].no)
+                    reg(it.items[0].opNo)
+                }, {
+                    _status.postValue(null)
+                    showMessage(it.toString())
+                }
+            ).adds()
+    }
+
+    private val _rpDetail = MutableLiveData<List<ItemsItem2>>()
+    val rpDetail: LiveData<List<ItemsItem2>> = _rpDetail
+
+    fun rpDetail(opNo: String, rpNo: String) {
+        HttpManager.getApi(MyApplication.appComponent1, Service::class.java)
+            .rpDetail(opNo, rpNo)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+            .subscribe(
+                {
+                    _rpDetail.postValue(it.items)
+                }, {
+                    _rpDetail.postValue(null)
+                    showMessage(it.toString())
+                }
+            ).adds()
+    }
+
+    private val _regM = MutableLiveData<ItemsItem3>()
+    val regM: LiveData<ItemsItem3> = _regM
+    fun reg(opNo: String) {
+        HttpManager.getApi(MyApplication.appComponent1, Service::class.java)
+            .reg(opNo)
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+            .subscribe(
+                {
+                    _regM.postValue(it?.items?.get(0))
+                }, {
+                    _regM.postValue(null)
+                    showMessage(it.toString())
+                }
+            ).adds()
+    }
+
+    private val _usages = MutableLiveData<List<ItemsItem5>>()
+    val usages: LiveData<List<ItemsItem5>> = _usages
+    fun drugUsage() {
+        HttpManager.getApi(MyApplication.appComponent2, Service::class.java)
+            .drugUsage()
+            .subscribeOn(Schedulers.io())
+            .observeOn(AndroidSchedulers.mainThread())
+            .subscribe(
+                {
+                    _usages.postValue(it.items)
+                }, {
+                    _usages.postValue(null)
+                    showMessage(it.toString())
+                }
+            ).adds()
+    }
+
+}

+ 40 - 0
app/src/main/java/com/bjca/hp/acupuncture/viewmodel/WelcomeVM.kt

@@ -0,0 +1,40 @@
+package com.bjca.hp.acupuncture.viewmodel
+
+import com.bjca.hp.acupuncture.model.ItemsItem
+import com.bjca.hp.acupuncture.repository.Service
+import com.xuqm.base.di.manager.HttpManager
+import com.xuqm.base.extensions.showMessage
+import com.xuqm.base.viewmodel.BaseListViewModel
+import com.xuqm.base.viewmodel.callback.Response
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.schedulers.Schedulers
+
+/***************************************************************************
+ * <pre></pre>
+ * @文件名称:  WelcomeVM
+ * @包   路   径:  com.bjca.hp.acupuncture.viewmodel
+ * @版权所有:北京数字医信责任有限公司 (C) 2022
+ *
+ * @类描述:
+ * @版本: V1.0
+ * @创建人: xuqm
+ * @创建时间:2022/6/17 下午 02:33
+ * @修改记录:
+ */
+class WelcomeVM : BaseListViewModel<ItemsItem>() {
+    override fun loadData(page: Int, onResponse: Response<ItemsItem>) {
+        add(
+            HttpManager.getApi(Service::class.java)
+                .standard(page)
+                .subscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(
+                    {
+                        onResponse.onResponse(it.items)
+                    }, {
+                        onResponse.onResponse(null)
+//                        showMessage(it)
+                    }
+                ))
+    }
+}

+ 22 - 0
app/src/main/java/com/bjca/hp/acupuncture/widget/ColorUser.java

@@ -0,0 +1,22 @@
+package com.bjca.hp.acupuncture.widget;
+
+import androidx.annotation.ColorInt;
+
+/***************************************************************************
+ * <pre></pre>
+ * @文件名称: ColorUser
+ * @包 路   径:  com.bjca.hp.acupuncture.widget
+ * @版权所有:北京数字医信责任有限公司 (C) 2022
+ *
+ * @类描述:
+ * @版本: V1.0
+ * @创建人: xuqm
+ * @创建时间:2022/1/19 上午 10:58
+ * @修改记录:
+ */
+public class ColorUser {
+    @ColorInt
+    public static final int ce = 0xFF04E8EE;
+    @ColorInt
+    public static final int cs = 0x003A6FF2;
+}

+ 11 - 0
app/src/main/res/drawable/bg_item_welcome.xml

@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <stroke android:width="0.5dp" android:color="#00000014" />
+            <corners android:radius="5dp" />
+            <solid android:color="@color/white"/>
+        </shape>
+
+    </item>
+</selector>

+ 10 - 0
app/src/main/res/drawable/bg_main.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="1dp" />
+            <solid android:color="#F1F9F5"/>
+        </shape>
+
+    </item>
+</selector>

+ 10 - 0
app/src/main/res/drawable/bg_main_btn.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="2dp" />
+            <solid android:color="#BA7D45" />
+        </shape>
+
+    </item>
+</selector>

+ 10 - 0
app/src/main/res/drawable/bg_main_item_content.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:bottomRightRadius="1dp" android:topRightRadius="1dp" />
+            <solid android:color="#FFFFFF" />
+        </shape>
+
+    </item>
+</selector>

+ 10 - 0
app/src/main/res/drawable/bg_main_item_index.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:bottomLeftRadius="1dp" android:topLeftRadius="1dp" />
+            <solid android:color="#BA7D45" />
+        </shape>
+
+    </item>
+</selector>

+ 10 - 0
app/src/main/res/drawable/bg_main_tag.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android">
+    <item>
+        <shape android:shape="rectangle">
+            <corners android:radius="1dp" />
+            <stroke android:width="1dp" android:color="#5EA564" />
+        </shape>
+
+    </item>
+</selector>

+ 698 - 0
app/src/main/res/layout/activity_main.xml

@@ -0,0 +1,698 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#BA7D45"
+        tools:context=".ui.MainActivity">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="20dp"
+            android:gravity="center"
+            android:text="联创明医医院智慧药柜"
+            android:textColor="@color/white"
+            android:textSize="16sp"
+            android:textStyle="bold"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginHorizontal="15dp"
+            android:layout_marginTop="76dp"
+            android:background="@drawable/bg_main"
+            android:orientation="vertical"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="24dp"
+                android:background="@drawable/bg_main"
+                android:gravity="center"
+                android:orientation="horizontal"
+                app:layout_constraintTop_toTopOf="parent">
+
+                <TextView
+                    android:id="@+id/name"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="广州黄埔区明医医院处方笺"
+                    android:textColor="#202020"
+                    android:textSize="14sp"
+                    android:textStyle="bold" />
+
+                <TextView
+                    android:id="@+id/tags"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_marginStart="17.5dp"
+                    android:background="@drawable/bg_main_tag"
+                    android:paddingHorizontal="7.5dp"
+                    android:paddingVertical="4.5dp"
+                    android:text="普通"
+                    android:textColor="#5EA564" />
+
+            </LinearLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="20dp"
+                android:orientation="horizontal"
+                android:paddingHorizontal="15dp">
+
+                <LinearLayout
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="3"
+                    android:orientation="vertical">
+
+                    <androidx.constraintlayout.widget.ConstraintLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content">
+
+
+                        <View
+                            android:layout_width="0dp"
+                            android:layout_height="4dp"
+                            android:background="#BA7D45"
+                            app:layout_constraintBottom_toBottomOf="@+id/title1"
+                            app:layout_constraintEnd_toEndOf="@+id/title1"
+                            app:layout_constraintStart_toStartOf="@+id/title1" />
+
+                        <TextView
+                            android:id="@+id/title1"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="患者信息"
+                            android:textColor="#ff202020"
+                            android:textSize="13sp"
+                            android:textStyle="bold"
+                            app:layout_constraintEnd_toEndOf="parent"
+                            app:layout_constraintStart_toStartOf="parent"
+                            app:layout_constraintTop_toTopOf="parent" />
+                    </androidx.constraintlayout.widget.ConstraintLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="10dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n1"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="姓名:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v1"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="12dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n2"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="性别:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v2"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="12dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n3"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="年龄:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v3"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="12dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n4"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="电话:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v4"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="12dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n5"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="地址:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v5"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+                </LinearLayout>
+
+                <LinearLayout
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="4"
+                    android:orientation="vertical">
+
+                    <androidx.constraintlayout.widget.ConstraintLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content">
+
+
+                        <View
+                            android:layout_width="0dp"
+                            android:layout_height="4dp"
+                            android:background="#BA7D45"
+                            app:layout_constraintBottom_toBottomOf="@+id/title2"
+                            app:layout_constraintEnd_toEndOf="@+id/title2"
+                            app:layout_constraintStart_toStartOf="@+id/title2" />
+
+                        <TextView
+                            android:id="@+id/title2"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="就诊信息"
+                            android:textColor="#ff202020"
+                            android:textSize="13sp"
+                            android:textStyle="bold"
+                            app:layout_constraintEnd_toEndOf="parent"
+                            app:layout_constraintStart_toStartOf="parent"
+                            app:layout_constraintTop_toTopOf="parent" />
+                    </androidx.constraintlayout.widget.ConstraintLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="10dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n6"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="就诊卡号:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v6"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="12dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n7"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="就诊科别:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v7"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="12dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n8"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="诊断结果:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v8"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="12dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n9"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="就诊日期:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v9"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                </LinearLayout>
+
+                <LinearLayout
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="4"
+                    android:orientation="vertical">
+
+                    <androidx.constraintlayout.widget.ConstraintLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content">
+
+
+                        <View
+                            android:layout_width="0dp"
+                            android:layout_height="4dp"
+                            android:background="#BA7D45"
+                            app:layout_constraintBottom_toBottomOf="@+id/title3"
+                            app:layout_constraintEnd_toEndOf="@+id/title3"
+                            app:layout_constraintStart_toStartOf="@+id/title3" />
+
+                        <TextView
+                            android:id="@+id/title3"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="医疗信息"
+                            android:textColor="#ff202020"
+                            android:textSize="13sp"
+                            android:textStyle="bold"
+                            app:layout_constraintEnd_toEndOf="parent"
+                            app:layout_constraintStart_toStartOf="parent"
+                            app:layout_constraintTop_toTopOf="parent" />
+                    </androidx.constraintlayout.widget.ConstraintLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="10dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n10"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="医疗类别:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v10"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="12dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n11"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="医疗/医保卡:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v11"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <ImageView
+                        android:layout_width="129dp"
+                        android:layout_height="50dp"
+                        android:layout_marginTop="12dp"
+                        android:scaleType="centerCrop"
+                        android:src="@color/ic_launcher_background" />
+
+                </LinearLayout>
+
+            </LinearLayout>
+
+            <androidx.constraintlayout.widget.ConstraintLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginHorizontal="15dp"
+                android:layout_marginTop="20dp">
+
+
+                <View
+                    android:layout_width="0dp"
+                    android:layout_height="4dp"
+                    android:background="#BA7D45"
+                    app:layout_constraintBottom_toBottomOf="@+id/title4"
+                    app:layout_constraintEnd_toEndOf="@+id/title4"
+                    app:layout_constraintStart_toStartOf="@+id/title4" />
+
+                <TextView
+                    android:id="@+id/title4"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="药品及使用说明"
+                    android:textColor="#ff202020"
+                    android:textSize="13sp"
+                    android:textStyle="bold"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+            </androidx.constraintlayout.widget.ConstraintLayout>
+
+            <com.xuqm.base.view.MaxLimitRecyclerView
+                android:id="@+id/list"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="10dp"
+                android:maxLength="6"
+                android:paddingHorizontal="15dp"
+                app:limit_maxHeight="350dp" />
+
+            <androidx.constraintlayout.widget.ConstraintLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginHorizontal="15dp"
+                android:layout_marginTop="20dp">
+
+
+                <View
+                    android:layout_width="0dp"
+                    android:layout_height="4dp"
+                    android:background="#BA7D45"
+                    app:layout_constraintBottom_toBottomOf="@+id/title5"
+                    app:layout_constraintEnd_toEndOf="@+id/title5"
+                    app:layout_constraintStart_toStartOf="@+id/title5" />
+
+                <TextView
+                    android:id="@+id/title5"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="药品及使用说明"
+                    android:textColor="#ff202020"
+                    android:textSize="13sp"
+                    android:textStyle="bold"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent" />
+            </androidx.constraintlayout.widget.ConstraintLayout>
+
+            <LinearLayout
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="10dp"
+                android:orientation="horizontal"
+                android:paddingHorizontal="15dp">
+
+                <LinearLayout
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:orientation="vertical">
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n12"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="药师:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v12"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="12dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n13"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="审方:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v13"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+                </LinearLayout>
+
+                <LinearLayout
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:orientation="vertical">
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n14"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="医师:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v14"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="12dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n15"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="调配:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v15"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="智慧药柜"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+                </LinearLayout>
+
+                <LinearLayout
+                    android:layout_width="0dp"
+                    android:layout_height="wrap_content"
+                    android:layout_weight="1"
+                    android:orientation="vertical">
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n16"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="药品金额:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v16"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+
+                    <LinearLayout
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginTop="12dp"
+                        android:gravity="center_vertical"
+                        android:orientation="horizontal">
+
+                        <TextView
+                            android:id="@+id/n17"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="核对发药:"
+                            android:textColor="#ff848484"
+                            android:textSize="11sp" />
+
+                        <TextView
+                            android:id="@+id/v17"
+                            android:layout_width="wrap_content"
+                            android:layout_height="wrap_content"
+                            android:text="--"
+                            android:textColor="#ff202020"
+                            android:textSize="11sp" />
+                    </LinearLayout>
+                </LinearLayout>
+
+
+
+            </LinearLayout>
+
+            <TextView
+                android:id="@+id/btn"
+                android:layout_width="170dp"
+                android:layout_height="50dp"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginTop="45dp"
+                android:layout_marginBottom="45dp"
+                android:background="@drawable/bg_main_btn"
+                android:gravity="center"
+                android:text="确认发药"
+                android:textColor="@color/white"
+                android:textSize="14sp"
+                android:textStyle="bold" />
+        </LinearLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 167 - 0
app/src/main/res/layout/activity_setting.xml

@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout>
+
+    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:orientation="vertical"
+        tools:context=".ui.SettingActivity">
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="40dp"
+            android:gravity="center"
+            android:text="开指定柜门"
+            android:textColor="@color/black"
+            android:textSize="18sp" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="68dp"
+            android:layout_marginTop="10dp"
+            android:background="@drawable/bg_button"
+            android:orientation="horizontal"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <EditText
+                android:id="@+id/a11"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:hint="柜号(1.2.3)"
+                android:inputType="number" />
+
+            <EditText
+                android:id="@+id/a12"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:hint="窗口号"
+                android:inputType="number" />
+
+            <TextView
+                android:id="@+id/a13"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/bg_button_red"
+                android:gravity="center"
+                android:text="开门"
+                android:textColor="@color/white"
+                android:textSize="18sp" />
+
+        </LinearLayout>
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="40dp"
+            android:gravity="center"
+            android:text="开指定指定范围内柜门"
+            android:textColor="@color/black"
+            android:textSize="18sp" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="68dp"
+            android:layout_marginTop="10dp"
+            android:background="@drawable/bg_button"
+            android:orientation="horizontal"
+            app:layout_constraintTop_toTopOf="parent">
+
+            <EditText
+                android:id="@+id/a21"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:hint="柜号(1.2.3)"
+                android:inputType="number" />
+
+            <EditText
+                android:id="@+id/a22"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:hint="开始窗口号"
+                android:inputType="number" />
+
+            <EditText
+                android:id="@+id/a23"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:hint="结束窗口号"
+                android:inputType="number" />
+
+            <TextView
+                android:id="@+id/a24"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/bg_button_red"
+                android:gravity="center"
+                android:text="开门"
+                android:textColor="@color/white"
+                android:textSize="18sp" />
+
+        </LinearLayout>
+
+        <TextView
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="40dp"
+            android:gravity="center"
+            android:text="一键开门"
+            android:textColor="@color/black"
+            android:textSize="18sp" />
+
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="68dp"
+            android:layout_marginTop="10dp"
+            android:background="@drawable/bg_button"
+            android:orientation="horizontal"
+            app:layout_constraintTop_toTopOf="parent">
+
+
+            <TextView
+                android:id="@+id/a31"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/bg_button_red"
+                android:gravity="center"
+                android:text="一号柜"
+                android:textColor="@color/white"
+                android:textSize="18sp" />
+
+            <TextView
+                android:id="@+id/a32"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:layout_marginHorizontal="10dp"
+                android:background="@drawable/bg_button_red"
+                android:gravity="center"
+                android:text="二号柜"
+                android:textColor="@color/white"
+                android:textSize="18sp" />
+
+            <TextView
+                android:id="@+id/a33"
+                android:layout_width="0dp"
+                android:layout_height="match_parent"
+                android:layout_weight="1"
+                android:background="@drawable/bg_button_red"
+                android:gravity="center"
+                android:text="三号柜"
+                android:textColor="@color/white"
+                android:textSize="18sp" />
+
+        </LinearLayout>
+
+    </LinearLayout>
+</layout>

+ 30 - 0
app/src/main/res/layout/activity_test.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        tools:context=".ui.TestActivity">
+
+        <EditText
+            android:id="@+id/edit_url"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="150dp"
+            android:text="https://web.sdk.qcloud.com/trtc/webrtc/demo/quick-demo-vue3-ts/index.html#/"
+            android:textSize="21sp"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <Button
+            android:id="@+id/btn"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="20dp"
+            android:text="进入页面"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/edit_url" />
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 50 - 0
app/src/main/res/layout/activity_welcome.xml

@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        xmlns:tools="http://schemas.android.com/tools"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="#BA7D45"
+        tools:context=".ui.WelcomeActivity">
+
+        <TextView
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="20dp"
+            android:gravity="center"
+            android:text="联创明医医院智慧药柜"
+            android:textColor="@color/white"
+            android:textSize="16sp"
+            android:textStyle="bold"
+            app:layout_constraintTop_toTopOf="parent" />
+
+
+        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+            android:id="@+id/baseRefreshLayout"
+            android:layout_width="match_parent"
+            android:layout_marginTop="20dp"
+            android:layout_marginBottom="34dp"
+            android:layout_height="0dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintTop_toBottomOf="@+id/title">
+
+            <com.xuqm.base.view.EmptyView
+                android:id="@+id/baseEmptyView"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent">
+
+                <androidx.recyclerview.widget.RecyclerView
+                    android:id="@+id/baseRecyclerView"
+                    android:layout_width="match_parent"
+                    android:layout_height="match_parent"
+                    android:overScrollMode="never" />
+
+            </com.xuqm.base.view.EmptyView>
+
+        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 224 - 0
app/src/main/res/layout/item_main.xml

@@ -0,0 +1,224 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="50dp">
+
+        <TextView
+            android:id="@+id/index"
+            android:layout_width="25dp"
+            android:layout_height="match_parent"
+            android:background="@drawable/bg_main_item_index"
+            android:gravity="center"
+            android:text="1"
+            android:textColor="@color/white"
+            android:textSize="12sp"
+            app:layout_constraintStart_toStartOf="parent" />
+
+        <LinearLayout
+            android:layout_width="0dp"
+            android:layout_height="match_parent"
+            android:background="@drawable/bg_main_item_content"
+            android:orientation="horizontal"
+            android:paddingHorizontal="6dp"
+            android:gravity="center_vertical"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintStart_toEndOf="@+id/index">
+
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="25"
+                android:orientation="vertical">
+
+                <TextView
+                    android:id="@+id/n1"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="药品名称"
+                    android:textColor="#ffacabab"
+                    android:textSize="10sp" />
+
+                <TextView
+                    android:id="@+id/v1"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:layout_marginTop="6dp"
+                    android:lines="1"
+                    android:text="药品名称药品名称药品名称药品名称药品名称药品名称药品名称"
+                    android:textColor="#ff202020"
+                    android:textSize="12sp" />
+
+
+            </LinearLayout>
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="18"
+                android:orientation="vertical">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/n2"
+                    android:text="规格"
+                    android:textColor="#ffacabab"
+                    android:textSize="10sp" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:layout_marginTop="6dp"
+                    android:lines="1"
+                    android:text="0.5g*10粒"
+                    android:id="@+id/v2"
+                    android:textColor="#ff202020"
+                    android:textSize="12sp" />
+
+
+            </LinearLayout>
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="9"
+                android:orientation="vertical">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:id="@+id/n3"
+                    android:layout_height="wrap_content"
+                    android:text="数量"
+                    android:textColor="#ffacabab"
+                    android:textSize="10sp" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:layout_marginTop="6dp"
+                    android:lines="1"
+                    android:id="@+id/v3"
+                    android:text="1盒"
+                    android:textColor="#ff202020"
+                    android:textSize="12sp" />
+
+
+            </LinearLayout>
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="12"
+                android:orientation="vertical">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/n4"
+                    android:text="使用方法"
+                    android:textColor="#ffacabab"
+                    android:textSize="10sp" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:layout_marginTop="6dp"
+                    android:lines="1"
+                    android:text="口服"
+                    android:id="@+id/v4"
+                    android:textColor="#ff202020"
+                    android:textSize="12sp" />
+
+
+            </LinearLayout>
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="12"
+                android:orientation="vertical">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:id="@+id/n5"
+                    android:layout_height="wrap_content"
+                    android:text="用量"
+                    android:textColor="#ffacabab"
+                    android:textSize="10sp" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:layout_marginTop="6dp"
+                    android:lines="1"
+                    android:id="@+id/v5"
+                    android:text="6g"
+                    android:textColor="#ff202020"
+                    android:textSize="12sp" />
+
+
+            </LinearLayout>
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="13"
+                android:orientation="vertical">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="频次"
+                    android:id="@+id/n6"
+                    android:textColor="#ffacabab"
+                    android:textSize="10sp" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:layout_marginTop="6dp"
+                    android:lines="1"
+                    android:id="@+id/v6"
+                    android:text="每日1次"
+                    android:textColor="#ff202020"
+                    android:textSize="12sp" />
+
+
+            </LinearLayout>
+            <LinearLayout
+                android:layout_width="0dp"
+                android:layout_height="wrap_content"
+                android:layout_weight="10"
+                android:orientation="vertical">
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:id="@+id/n7"
+                    android:text="使用周期"
+                    android:textColor="#ffacabab"
+                    android:textSize="10sp" />
+
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:ellipsize="end"
+                    android:layout_marginTop="6dp"
+                    android:lines="1"
+                    android:id="@+id/v7"
+                    android:text="2天"
+                    android:textColor="#ff202020"
+                    android:textSize="12sp" />
+
+
+            </LinearLayout>
+
+        </LinearLayout>
+
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

+ 83 - 0
app/src/main/res/layout/item_welcome.xml

@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:layout_width="match_parent"
+        android:layout_height="130dp"
+        android:background="@drawable/bg_item_welcome">
+
+        <TextView
+            android:id="@+id/index"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="10dp"
+            android:layout_marginTop="10dp"
+            android:background="#BA7D45"
+            android:paddingHorizontal="7.5dp"
+            android:paddingVertical="3.5dp"
+            android:text="01"
+            android:textColor="@color/white"
+            android:textSize="13sp"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent" />
+
+        <TextView
+            android:id="@+id/name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:text="活血止痛胶囊(10mg/盒)"
+            android:textColor="@color/black"
+            android:textSize="13sp"
+            android:textStyle="bold"
+            app:layout_constraintStart_toStartOf="@+id/index"
+            app:layout_constraintTop_toBottomOf="@+id/index" />
+
+        <TextView
+            android:id="@+id/hint"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:text="广东联合制药"
+            android:textColor="#A4ADBA"
+            android:textSize="10sp"
+            app:layout_constraintStart_toStartOf="@+id/index"
+            app:layout_constraintTop_toBottomOf="@+id/name" />
+
+        <TextView
+            android:id="@+id/number"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="15dp"
+            android:text="12.5"
+            android:textColor="#FF4600"
+            android:textStyle="bold"
+            android:textSize="18sp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintStart_toStartOf="@+id/index" />
+
+        <TextView
+            android:id="@+id/unit"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="元/盒"
+            android:textColor="#FF4600"
+            android:layout_marginBottom="3dp"
+            app:layout_constraintBottom_toBottomOf="@+id/number"
+            android:textSize="10sp"
+            app:layout_constraintStart_toEndOf="@+id/number" />
+        <TextView
+            android:id="@+id/balance"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="剩余:100盒"
+            android:textColor="#333333"
+            android:layout_marginBottom="3dp"
+            android:layout_marginEnd="10dp"
+            app:layout_constraintBottom_toBottomOf="@+id/number"
+            android:textSize="11sp"
+            app:layout_constraintEnd_toEndOf="parent" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+</layout>

BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xhdpi/ic_face.png


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xhdpi/ic_psw.png


BIN
app/src/main/res/mipmap-xxhdpi/bg_login.jpg


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
app/src/main/res/mipmap-xxhdpi/icon_arrow_blue.png


BIN
app/src/main/res/mipmap-xxhdpi/icon_login.png


BIN
app/src/main/res/mipmap-xxhdpi/login_bg.jpg


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


+ 3 - 0
app/src/main/res/values/arrays.xml

@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+</resources>

+ 12 - 0
app/src/main/res/values/colors.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <color name="colorPrimary">#33a6ff</color>
+    <color name="colorPrimaryDark">#00574B</color>
+    <color name="colorAccent">#7e53c5</color>
+    <color name="bg_lapss_line">#BBBBBB</color>
+    <color name="black_filter_10">#10000000</color>
+    <color name="textcolor">#797979</color>
+    <color name="textcolor_blue">#33a6ff</color>
+
+    <color name="backgroundColor">#10142B</color>
+</resources>

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Test</string>
+</resources>

+ 20 - 0
app/src/main/res/values/styles.xml

@@ -0,0 +1,20 @@
+<resources>
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+        <item name="android:windowTranslucentStatus">true</item>
+        <item name="backgroundColor">#F1F4F7</item>
+    </style>
+
+    <style name="DialogTheme" parent="Theme.AppCompat.Light.Dialog">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
+        <item name="android:windowLightStatusBar">false</item>
+        <item name="android:windowCloseOnTouchOutside">false</item>
+    </style>
+
+
+</resources>

+ 6 - 0
app/src/main/res/xml/filepaths.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<paths>
+    <external-path
+        name="external"
+        path="." />
+</paths>

+ 2 - 0
base/.gitattributes

@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto

+ 83 - 0
base/.gitignore

@@ -0,0 +1,83 @@
+# Built application files
+*.apk
+*.ap_
+*.aab
+
+# Files for the ART/Dalvik VM
+*.dex
+
+# Java class files
+*.class
+
+# Generated files
+bin/
+gen/
+out/
+#  Uncomment the following line in case you need and you don't have the release build type files in your app
+# release/
+
+# Gradle files
+.gradle/
+build/
+
+# Local configuration file (sdk path, etc)
+local.properties
+
+# Proguard folder generated by Eclipse
+proguard/
+
+# Log Files
+*.log
+
+# Android Studio Navigation editor temp files
+.navigation/
+
+# Android Studio captures folder
+captures/
+
+# IntelliJ
+*.iml
+.idea/workspace.xml
+.idea/tasks.xml
+.idea/gradle.xml
+.idea/assetWizardSettings.xml
+.idea/dictionaries
+.idea/libraries
+# Android Studio 3 in .gitignore file.
+.idea/caches
+.idea/modules.xml
+# Comment next line if keeping position of elements in Navigation Editor is relevant for you
+.idea/navEditor.xml
+
+# Keystore files
+# Uncomment the following lines if you do not want to check your keystore files in.
+#*.jks
+#*.keystore
+
+# External native build folder generated in Android Studio 2.2 and later
+.externalNativeBuild
+
+# Google Services (e.g. APIs or Firebase)
+# google-services.json
+
+# Freeline
+freeline.py
+freeline/
+freeline_project_description.json
+
+# fastlane
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output
+fastlane/readme.md
+
+# Version control
+vcs.xml
+
+# lint
+lint/intermediates/
+lint/generated/
+lint/outputs/
+lint/tmp/
+# lint/reports/

+ 184 - 0
base/README.md

@@ -0,0 +1,184 @@
+[TOC]
+
+
+# WebSocket
+
+``````kotlin
+WebSocketHandler.getInstance("ws://192.168.3.20:8765")
+``````
+
+# 线程
+
+## UI线程执行
+
+````kotlin
+runOnUiThread { "提示信息".showMessage() }
+````
+
+````kotlin
+App.getInstance().runOnUiThread() {}
+````
+
+## 延时执行
+
+```kotlin
+App.getInstance().runOnUiThreadDelay({},1100) 
+```
+
+# 常用工具
+
+## Toast
+
+````kotlin
+"连接完成".showMessage()
+````
+
+````kotlin
+ToolsHelper.showMessage("")
+````
+
+## Log
+
+````kotlin
+"".loge()
+````
+
+````kotlin
+"".log()
+````
+
+````kotlin
+LogHelper.d("")
+````
+
+
+
+# 常用方法
+
+## 双击退出
+
+```kotlin
+
+    private var oldTime = 0L
+    override fun onBackPressed() {
+        val newTime = System.currentTimeMillis()
+        if (newTime - oldTime < 1500 && oldTime != 0L)
+            AppManager.getInstance().exit()
+        else {
+            oldTime = newTime
+            ToolsHelper.showMessage("双击退出")
+        }
+    }
+```
+
+
+
+
+
+
+# 界面
+
+> 所有界面继承`BaseFragment`,`BaseActivity`,`BaseListActivity`等
+>
+> 页面`layout`跟节点必须为`layout`
+
+```xml
+<layout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto">
+</layout>
+```
+
+## 列表页面
+
+### 纯列表
+
+> `BaseListActivity`
+
+### 自定义布局列表
+
+> `BaseListFormLayoutActivity` 
+>
+> 布局列表部分必须使用下面的方法和id
+
+```xml
+<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
+    android:id="@+id/baseRefreshLayout"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <com.xuqm.base.view.EmptyView
+        android:id="@+id/baseEmptyView"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+        <androidx.recyclerview.widget.RecyclerView
+            android:id="@+id/baseRecyclerView"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:overScrollMode="never" />
+
+    </com.xuqm.base.view.EmptyView>
+
+</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
+```
+
+## 界面控件使用
+
+```kotlin
+binding.btn1.setOnClickListener {
+    
+}
+```
+
+## 导航栏
+
+> 使用base自带导航栏的情况下,可以操控对应控件
+
+```kotlin
+baseBinding.baseToolbar.backBtn.setOnClickListener {}
+```
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ 94 - 0
base/build.gradle

@@ -0,0 +1,94 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'maven-publish'
+
+// 声明aar包的版本号
+def aarVersion = "0.0.1.101"
+
+
+android {
+    compileSdkVersion versions.compileSdk
+    buildToolsVersion versions.buildTools
+
+    defaultConfig {
+        minSdkVersion versions.minSdk
+        targetSdkVersion versions.targetSdk
+        flavorDimensions "versioncode"
+        buildConfigField("String", "APP_ID", "\"" + apps.applicationId + "\"")
+    }
+
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+    productFlavors productF
+    namespace 'com.xuqm.base'
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    api androidxDependencies
+    implementation 'androidx.appcompat:appcompat:1.3.0'
+    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
+
+
+    implementation 'org.jetbrains.anko:anko-commons:0.10.5'
+
+    annotationProcessor compilerDependencies
+    commonDependencies.each { k, v ->
+        api(v) {
+            //去除第三方的重复support库
+            exclude group: 'com.android.support'
+        }
+    }
+    //扫码二维码
+    api 'com.huawei.hms:scanplus:1.1.1.301'
+}
+// 这个是把源码打入aar包中的任务
+task sourceJar(type: Jar) {
+    archiveClassifier.set('sources')
+    from android.sourceSets.main.java.srcDirs
+}
+afterEvaluate {
+    publishing {
+        publications {
+            // 这里的debug名字是自己起的
+            release(MavenPublication) {
+                groupId = 'cn.org.bjca.trust.android'
+                artifactId = 'base'
+                version = aarVersion
+                // 这里除了有debug 还有release
+                from components.release
+                // 运行任务,把源码打进去
+                artifact sourceJar
+            }
+        }
+        // 添加仓库地址
+        repositories {
+            // 本地仓库
+//            mavenLocal()
+            // 当上传到远端仓库
+//            maven {
+//                allowInsecureProtocol true
+//                url("http://nexus.51trust.net/repository/android-hosted/")
+//                credentials {
+//                    username = "deployment"
+//                    password = "deployment123"
+//                }
+//          }
+            maven {
+                allowInsecureProtocol true
+                url("http://xuqinmin.com.cn:8081/repository/android-releases/")
+                credentials {
+                    username = "admin"
+                    password = "xuqinmin1022"
+                }
+            }
+        }
+    }
+}

+ 0 - 0
base/consumer-rules.pro


+ 21 - 0
base/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 27 - 0
base/src/main/AndroidManifest.xml

@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- 7.0之后安装apk、需要权限 -->
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+
+    <permission android:name="android.permission.INSTALL_PACKAGES" />
+
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.webkit.PermissionRequest" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+
+    <uses-permission android:name="android.webkit.resource.VIDEO_CAPTURE" />
+
+    <application>
+        <activity android:name=".web.XWebViewActivity" />
+    </application>
+
+</manifest>

+ 155 - 0
base/src/main/java/com/livinglifetechway/quickpermissions_kotlin/PermissionsManager.kt

@@ -0,0 +1,155 @@
+package com.livinglifetechway.quickpermissions_kotlin
+
+import android.content.Context
+import android.util.Log
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.Fragment
+import com.livinglifetechway.quickpermissions_kotlin.util.PermissionCheckerFragment
+import com.livinglifetechway.quickpermissions_kotlin.util.PermissionsUtil
+import com.livinglifetechway.quickpermissions_kotlin.util.QuickPermissionsRequest
+import com.livinglifetechway.quickpermissions_kotlin.util.QuickPermissionsOptions
+
+private const val TAG = "runWithPermissions"
+
+/**
+ * Injects code to ask for permissions before executing any code that requires permissions
+ * defined in the annotation
+ */
+fun Context?.runWithPermissions(
+        vararg permissions: String,
+        options: QuickPermissionsOptions = QuickPermissionsOptions(),
+        callback: () -> Unit
+): Any? {
+    return runWithPermissionsHandler(this, permissions, callback, options)
+}
+
+/**
+ * Injects code to ask for permissions before executing any code that requires permissions
+ * defined in the annotation
+ */
+fun Fragment?.runWithPermissions(
+        vararg permissions: String,
+        options: QuickPermissionsOptions = QuickPermissionsOptions(),
+        callback: () -> Unit
+): Any? {
+    return runWithPermissionsHandler(this, permissions, callback, options)
+}
+
+private fun runWithPermissionsHandler(target: Any?, permissions: Array<out String>, callback: () -> Unit, options: QuickPermissionsOptions): Nothing? {
+    Log.d(TAG, "runWithPermissions: start")
+
+    // get the permissions defined in annotation
+    Log.d(TAG, "runWithPermissions: permissions to check: $permissions")
+
+    // get target
+    if (target is AppCompatActivity || target is Fragment) {
+        Log.d(TAG, "runWithPermissions: context found")
+
+        val context = when (target) {
+            is Context -> target
+            is Fragment -> target.context
+            else -> null
+        }
+
+        // check if we have the permissions
+        if (PermissionsUtil.hasSelfPermission(context, arrayOf(*permissions))) {
+            Log.d(TAG, "runWithPermissions: already has required permissions. Proceed with the execution.")
+            callback()
+        } else {
+            // we don't have required permissions
+            // begin the permission request flow
+
+            Log.d(TAG, "runWithPermissions: doesn't have required permissions")
+
+            // check if we have permission checker fragment already attached
+
+            // support for AppCompatActivity and Activity
+            var permissionCheckerFragment = when (context) {
+            // for app compat activity
+                is AppCompatActivity -> context.supportFragmentManager?.findFragmentByTag(PermissionCheckerFragment::class.java.canonicalName) as PermissionCheckerFragment?
+            // for support fragment
+                is Fragment -> context.childFragmentManager.findFragmentByTag(PermissionCheckerFragment::class.java.canonicalName) as PermissionCheckerFragment?
+            // else return null
+                else -> null
+            }
+
+            // check if permission check fragment is added or not
+            // if not, add that fragment
+            if (permissionCheckerFragment == null) {
+                Log.d(TAG, "runWithPermissions: adding headless fragment for asking permissions")
+                permissionCheckerFragment = PermissionCheckerFragment.newInstance()
+                when (context) {
+                    is AppCompatActivity -> {
+                        context.supportFragmentManager.beginTransaction().apply {
+                            add(permissionCheckerFragment, PermissionCheckerFragment::class.java.canonicalName)
+                            commit()
+                        }
+                        // make sure fragment is added before we do any context based operations
+                        context.supportFragmentManager?.executePendingTransactions()
+                    }
+                    is Fragment -> {
+                        // this does not work at the moment
+                        context.childFragmentManager.beginTransaction().apply {
+                            add(permissionCheckerFragment, PermissionCheckerFragment::class.java.canonicalName)
+                            commit()
+                        }
+                        // make sure fragment is added before we do any context based operations
+                        context.childFragmentManager.executePendingTransactions()
+                    }
+                }
+            }
+
+            // set callback to permission checker fragment
+            permissionCheckerFragment.setListener(object : PermissionCheckerFragment.QuickPermissionsCallback {
+                override fun onPermissionsGranted(quickPermissionsRequest: QuickPermissionsRequest?) {
+                    Log.d(TAG, "runWithPermissions: got permissions")
+                    try {
+                        callback()
+                    } catch (throwable: Throwable) {
+                        throwable.printStackTrace()
+                    }
+                }
+
+                override fun onPermissionsDenied(quickPermissionsRequest: QuickPermissionsRequest?) {
+                    quickPermissionsRequest?.permissionsDeniedMethod?.invoke(quickPermissionsRequest)
+                }
+
+                override fun shouldShowRequestPermissionsRationale(quickPermissionsRequest: QuickPermissionsRequest?) {
+                    quickPermissionsRequest?.rationaleMethod?.invoke(quickPermissionsRequest)
+                }
+
+                override fun onPermissionsPermanentlyDenied(quickPermissionsRequest: QuickPermissionsRequest?) {
+                    quickPermissionsRequest?.permanentDeniedMethod?.invoke(quickPermissionsRequest)
+                }
+            })
+
+            // create permission request instance
+            val permissionRequest = QuickPermissionsRequest(permissionCheckerFragment, arrayOf(*permissions))
+            permissionRequest.handleRationale = options.handleRationale
+            permissionRequest.handlePermanentlyDenied = options.handlePermanentlyDenied
+            permissionRequest.rationaleMessage = if (options.rationaleMessage.isBlank())
+                "These permissions are required to perform this feature. Please allow us to use this feature. "
+            else
+                options.rationaleMessage
+            permissionRequest.permanentlyDeniedMessage = if (options.permanentlyDeniedMessage.isBlank())
+                "Some permissions are permanently denied which are required to perform this operation. Please open app settings to grant these permissions."
+            else
+                options.permanentlyDeniedMessage
+            permissionRequest.rationaleMethod = options.rationaleMethod
+            permissionRequest.permanentDeniedMethod = options.permanentDeniedMethod
+            permissionRequest.permissionsDeniedMethod = options.permissionsDeniedMethod
+
+            // begin the flow by requesting permissions
+            permissionCheckerFragment.setRequestPermissionsRequest(permissionRequest)
+
+            // start requesting permissions for the first time
+            permissionCheckerFragment.requestPermissionsFromUser()
+        }
+    } else {
+        // context is null
+        // cannot handle the permission checking from the any class other than AppCompatActivity/Fragment
+        // crash the app RIGHT NOW!
+        throw IllegalStateException("Found " + target!!::class.java.canonicalName + " : No support from any classes other than AppCompatActivity/Fragment")
+    }
+    return null
+}

+ 237 - 0
base/src/main/java/com/livinglifetechway/quickpermissions_kotlin/util/PermissionCheckerFragment.kt

@@ -0,0 +1,237 @@
+package com.livinglifetechway.quickpermissions_kotlin.util
+
+
+import android.content.Intent
+import android.content.pm.PackageManager
+import android.net.Uri.fromParts
+import android.os.Bundle
+import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
+import android.util.Log
+import androidx.core.app.ActivityCompat
+import androidx.fragment.app.Fragment
+import org.jetbrains.anko.alert
+
+/**
+ * This fragment holds the single permission request and holds it until the flow is completed
+ */
+class PermissionCheckerFragment : Fragment() {
+
+    private var quickPermissionsRequest: QuickPermissionsRequest? = null
+
+    interface QuickPermissionsCallback {
+        fun shouldShowRequestPermissionsRationale(quickPermissionsRequest: QuickPermissionsRequest?)
+        fun onPermissionsGranted(quickPermissionsRequest: QuickPermissionsRequest?)
+        fun onPermissionsPermanentlyDenied(quickPermissionsRequest: QuickPermissionsRequest?)
+        fun onPermissionsDenied(quickPermissionsRequest: QuickPermissionsRequest?)
+    }
+
+    companion object {
+        private const val TAG = "QuickPermissionsKotlin"
+        private const val PERMISSIONS_REQUEST_CODE = 199
+        fun newInstance(): PermissionCheckerFragment = PermissionCheckerFragment()
+    }
+
+    private var mListener: QuickPermissionsCallback? = null
+
+    fun setListener(listener: QuickPermissionsCallback) {
+        mListener = listener
+        Log.d(TAG, "onCreate: listeners set")
+    }
+
+    private fun removeListener() {
+        mListener = null
+    }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        Log.d(TAG, "onCreate: permission fragment created")
+    }
+
+    fun setRequestPermissionsRequest(quickPermissionsRequest: QuickPermissionsRequest?) {
+        this.quickPermissionsRequest = quickPermissionsRequest
+    }
+
+    private fun removeRequestPermissionsRequest() {
+        quickPermissionsRequest = null
+    }
+
+    fun clean() {
+        if (quickPermissionsRequest != null) {
+            // permission request flow is finishing
+            // let the caller receive callback about it
+            if (quickPermissionsRequest?.deniedPermissions?.size ?: 0 > 0)
+                mListener?.onPermissionsDenied(quickPermissionsRequest)
+
+            removeRequestPermissionsRequest()
+            removeListener()
+        } else {
+            Log.w(
+                TAG, "clean: QuickPermissionsRequest has already completed its flow. " +
+                        "No further callbacks will be called for the current flow."
+            )
+        }
+    }
+
+    fun requestPermissionsFromUser() {
+        if (quickPermissionsRequest != null) {
+            Log.d(TAG, "requestPermissionsFromUser: requesting permissions")
+            requestPermissions(
+                quickPermissionsRequest?.permissions.orEmpty(),
+                PERMISSIONS_REQUEST_CODE
+            )
+        } else {
+            Log.w(
+                TAG,
+                "requestPermissionsFromUser: QuickPermissionsRequest has already completed its flow. " +
+                        "Cannot request permissions again from the request received from the callback. " +
+                        "You can start the new flow by calling runWithPermissions() { } again."
+            )
+        }
+    }
+
+    override fun onRequestPermissionsResult(
+        requestCode: Int,
+        permissions: Array<String>,
+        grantResults: IntArray
+    ) {
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
+        Log.d(TAG, "passing callback")
+
+        // check if permissions granted
+        handlePermissionResult(permissions, grantResults)
+    }
+
+    /**
+     * Checks and takes the action based on permission results retrieved from onRequestPermissionsResult
+     * and from the settings activity
+     *
+     * @param permissions List of Permissions
+     * @param grantResults A list of permission result <b>Granted</b> or <b>Denied</b>
+     */
+    private fun handlePermissionResult(permissions: Array<String>, grantResults: IntArray) {
+        // add a check with the permissions list
+        // if the permissions list is empty, that means system has told that permissions request
+        // is invalid somehow or discarded the previous request
+        // this can happen in case when the multiple permissions requests are sent
+        // simultaneously to the system
+        if (permissions.isEmpty()) {
+            Log.w(
+                TAG,
+                "handlePermissionResult: Permissions result discarded. You might have called multiple permissions request simultaneously"
+            )
+            return
+        }
+
+        if (PermissionsUtil.hasSelfPermission(context, permissions)) {
+
+            // set the denied permissions to empty as all the permissions are granted
+            // this is required as clean will be called which can invoke on permissions denied
+            // if it finds some permissions in the denied list
+            quickPermissionsRequest?.deniedPermissions = emptyArray()
+
+            // we are good to go!
+            mListener?.onPermissionsGranted(quickPermissionsRequest)
+
+            // flow complete
+            clean()
+        } else {
+            // we are still missing permissions
+            val deniedPermissions = PermissionsUtil.getDeniedPermissions(permissions, grantResults)
+            quickPermissionsRequest?.deniedPermissions = deniedPermissions
+
+            // check if rationale dialog should be shown or not
+            var shouldShowRationale = true
+            var isPermanentlyDenied = false
+            for (i in 0 until deniedPermissions.size) {
+                val deniedPermission = deniedPermissions[i]
+                val rationale = shouldShowRequestPermissionRationale(deniedPermission)
+                if (!rationale) {
+                    shouldShowRationale = false
+                    isPermanentlyDenied = true
+                    break
+                }
+            }
+
+            if (quickPermissionsRequest?.handlePermanentlyDenied == true && isPermanentlyDenied) {
+
+                quickPermissionsRequest?.permanentDeniedMethod?.let {
+                    // get list of permanently denied methods
+                    quickPermissionsRequest?.permanentlyDeniedPermissions =
+                        PermissionsUtil.getPermanentlyDeniedPermissions(
+                            this,
+                            permissions,
+                            grantResults
+                        )
+                    mListener?.onPermissionsPermanentlyDenied(quickPermissionsRequest)
+                    return
+                }
+
+                activity?.alert {
+                    message = quickPermissionsRequest?.permanentlyDeniedMessage.orEmpty()
+                    positiveButton("SETTINGS") {
+                        openAppSettings()
+                    }
+                    negativeButton("CANCEL") {
+                        clean()
+                    }
+                }?.apply { isCancelable = false }?.show()
+                return
+            }
+
+            // if should show rationale dialog
+            if (quickPermissionsRequest?.handleRationale == true && shouldShowRationale) {
+
+                quickPermissionsRequest?.rationaleMethod?.let {
+                    mListener?.shouldShowRequestPermissionsRationale(quickPermissionsRequest)
+                    return
+                }
+
+                activity?.alert {
+                    message = quickPermissionsRequest?.rationaleMessage.orEmpty()
+                    positiveButton("TRY AGAIN") {
+                        requestPermissionsFromUser()
+                    }
+                    negativeButton("CANCEL") {
+                        clean()
+                    }
+                }?.apply { isCancelable = false }?.show()
+                return
+            }
+
+            // if handlePermanentlyDenied = false and handleRationale = false
+            // This will call permissionsDenied method
+            clean()
+        }
+    }
+
+    fun openAppSettings() {
+        if (quickPermissionsRequest != null) {
+            val intent = Intent(
+                ACTION_APPLICATION_DETAILS_SETTINGS,
+                fromParts("package", activity?.packageName, null)
+            )
+            //                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            startActivityForResult(intent, PERMISSIONS_REQUEST_CODE)
+        } else {
+            Log.w(
+                TAG,
+                "openAppSettings: QuickPermissionsRequest has already completed its flow. Cannot open app settings"
+            )
+        }
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        if (requestCode == PERMISSIONS_REQUEST_CODE) {
+            val permissions = quickPermissionsRequest?.permissions ?: emptyArray()
+            val grantResults = IntArray(permissions.size)
+            permissions.forEachIndexed { index, s ->
+                grantResults[index] = context?.let { ActivityCompat.checkSelfPermission(it, s) }
+                    ?: PackageManager.PERMISSION_DENIED
+            }
+
+            handlePermissionResult(permissions, grantResults)
+        }
+    }
+}

+ 54 - 0
base/src/main/java/com/livinglifetechway/quickpermissions_kotlin/util/PermissionsUtil.kt

@@ -0,0 +1,54 @@
+package com.livinglifetechway.quickpermissions_kotlin.util
+
+import android.app.Activity
+import android.content.Context
+import android.content.pm.PackageManager
+import androidx.core.app.ActivityCompat
+import androidx.fragment.app.Fragment
+
+/**
+ * Utility class that wraps access to the runtime permissions API in M and provides basic helper
+ * methods.
+ */
+object PermissionsUtil {
+
+    fun getDeniedPermissions(permissions: Array<String>, grantResults: IntArray): Array<String> =
+        permissions.filterIndexed { index, s ->
+            grantResults[index] == PackageManager.PERMISSION_DENIED
+        }.toTypedArray()
+
+    fun getPermanentlyDeniedPermissions(
+        fragment: Fragment,
+        permissions: Array<String>,
+        grantResults: IntArray
+    ): Array<String> =
+        permissions.filterIndexed { index, s ->
+            grantResults[index] == PackageManager.PERMISSION_DENIED && !fragment.shouldShowRequestPermissionRationale(
+                s
+            )
+        }.toTypedArray()
+
+    /**
+     * Returns true if the Activity has access to all given permissions.
+     * Always returns true on platforms below M.
+     *
+     * @see Activity.checkSelfPermission
+     */
+    fun hasSelfPermission(activity: Context?, permissions: Array<String>): Boolean {
+        // Verify that all required permissions have been granted
+        activity?.let {
+            for (permission in permissions) {
+                if (ActivityCompat.checkSelfPermission(
+                        activity,
+                        permission
+                    ) != PackageManager.PERMISSION_GRANTED
+                ) {
+                    return false
+                }
+            }
+        }
+
+        return true
+    }
+
+}

+ 11 - 0
base/src/main/java/com/livinglifetechway/quickpermissions_kotlin/util/QuickPermissionsOptions.kt

@@ -0,0 +1,11 @@
+package com.livinglifetechway.quickpermissions_kotlin.util
+
+data class QuickPermissionsOptions(
+        var handleRationale: Boolean = true,
+        var rationaleMessage: String = "",
+        var handlePermanentlyDenied: Boolean = true,
+        var permanentlyDeniedMessage: String = "",
+        var rationaleMethod: ((QuickPermissionsRequest) -> Unit)? = null,
+        var permanentDeniedMethod: ((QuickPermissionsRequest) -> Unit)? = null,
+        var permissionsDeniedMethod: ((QuickPermissionsRequest) -> Unit)? = null
+)

+ 30 - 0
base/src/main/java/com/livinglifetechway/quickpermissions_kotlin/util/QuickPermissionsRequest.kt

@@ -0,0 +1,30 @@
+package com.livinglifetechway.quickpermissions_kotlin.util
+
+data class QuickPermissionsRequest(
+        private var target: PermissionCheckerFragment,
+        var permissions: Array<String> = emptyArray(),
+        var handleRationale: Boolean = true,
+        var rationaleMessage: String = "",
+        var handlePermanentlyDenied: Boolean = true,
+        var permanentlyDeniedMessage: String = "",
+        internal var rationaleMethod: ((QuickPermissionsRequest) -> Unit)? = null,
+        internal var permanentDeniedMethod: ((QuickPermissionsRequest) -> Unit)? = null,
+        internal var permissionsDeniedMethod: ((QuickPermissionsRequest) -> Unit)? = null,
+        var deniedPermissions: Array<String> = emptyArray(),
+        var permanentlyDeniedPermissions: Array<String> = emptyArray()
+) {
+    /**
+     * Proceed with requesting permissions again with user request
+     */
+    fun proceed() = target.requestPermissionsFromUser()
+
+    /**
+     * Cancels the current permissions request flow
+     */
+    fun cancel() = target.clean()
+
+    /**
+     * In case of permissions permanently denied, request user to enable from app settings
+     */
+    fun openAppSettings() = target.openAppSettings()
+}

+ 111 - 0
base/src/main/java/com/xuqm/base/App.java

@@ -0,0 +1,111 @@
+package com.xuqm.base;
+
+import android.app.Application;
+import android.content.Context;
+import android.os.Handler;
+import android.util.DisplayMetrics;
+import android.view.WindowManager;
+
+import androidx.annotation.Nullable;
+
+import com.orhanobut.logger.AndroidLogAdapter;
+import com.orhanobut.logger.FormatStrategy;
+import com.orhanobut.logger.Logger;
+import com.orhanobut.logger.PrettyFormatStrategy;
+import com.xuqm.base.di.component.AppComponent;
+import com.xuqm.base.di.manager.HttpManager;
+
+public class App extends Application {
+
+    public AppComponent appComponent;
+    // 宽高
+    private int width = 0, height = 0;
+
+
+    private static App instance;
+
+    public static App getInstance() {
+        if (null == instance) {
+            synchronized (App.class) {
+                if (null == instance)
+                    instance = new App();
+            }
+        }
+        return instance;
+    }
+
+    public App() {
+        instance = this;
+        handler = new Handler();
+        appComponent = HttpManager.getAppComponent("");
+    }
+
+    //https://www.wanandroid.com/wxarticle/list/408/1/json
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        DisplayMetrics dm = new DisplayMetrics();
+        WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
+        if (null != wm) {
+            wm.getDefaultDisplay().getMetrics(dm);
+            width = dm.widthPixels;// 屏幕宽度
+            height = dm.heightPixels;// 屏幕高度
+        }
+
+        FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
+                .showThreadInfo(false)  // (Optional) Whether to show thread info or not. Default true
+                .methodCount(0)         // (Optional) How many method line to show. Default 2
+                .methodOffset(2)        // (Optional) Hides internal method calls up to offset. Default 5
+                //.logStrategy(customLog) // (Optional) Changes the log strategy to print out. Default LogCat
+                .tag("LogHttpInfo")   // (Optional) Global tag for every log. Default PRETTY_LOGGER
+                .build();
+
+        Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy) {
+            @Override
+            public boolean isLoggable(int priority, @Nullable String tag) {
+                return showLog();
+            }
+        });
+
+
+    }
+
+    /**
+     * 是否打印日志
+     *
+     * @return true-开启日志
+     */
+    public boolean showLog() {
+        return true;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    private final Handler handler;
+
+    /**
+     * 提交主线程处理
+     *
+     * @param runnable runnable
+     */
+    public void runOnUiThread(final Runnable runnable) {
+        handler.post(runnable);
+    }
+
+    /**
+     * 提交主线程,延时后处理
+     *
+     * @param runnable    runnable
+     * @param delayMillis 延时时间
+     */
+    public void runOnUiThreadDelay(final Runnable runnable, long delayMillis) {
+        handler.postDelayed(runnable, delayMillis);
+    }
+
+}

+ 222 - 0
base/src/main/java/com/xuqm/base/CrashHandler.java

@@ -0,0 +1,222 @@
+package com.xuqm.base;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Looper;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.xuqm.base.common.FileHelper;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.lang.reflect.Field;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+
+/**
+ * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
+ *
+ * @author user
+ */
+public class CrashHandler implements UncaughtExceptionHandler {
+
+    public static final String TAG = "CrashHandler";
+
+    // CrashHandler 实例
+    private static CrashHandler INSTANCE = new CrashHandler();
+
+    // 程序的 Context 对象
+    private Context mContext;
+
+    // 系统默认的 UncaughtException 处理类
+    private UncaughtExceptionHandler mDefaultHandler;
+
+    // 用来存储设备信息和异常信息
+    private Map<String, String> infos = new HashMap<String, String>();
+
+    // 用于格式化日期,作为日志文件名的一部分
+    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault());
+
+    /**
+     * 保证只有一个 CrashHandler 实例
+     */
+    private CrashHandler() {
+    }
+
+    /**
+     * 获取 CrashHandler 实例 ,单例模式
+     */
+    public static CrashHandler getInstance() {
+        return INSTANCE;
+    }
+
+    /**
+     * 初始化
+     *
+     * @param context
+     */
+    public void init(Context context) {
+        mContext = context;
+        // 获取系统默认的 UncaughtException 处理器
+        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+        // 设置该 CrashHandler 为程序的默认处理器
+        Thread.setDefaultUncaughtExceptionHandler(this);
+    }
+
+    /**
+     * 当 UncaughtException 发生时会转入该函数来处理
+     */
+    @Override
+    public void uncaughtException(Thread thread, Throwable ex) {
+        if (!handleException(ex) && mDefaultHandler != null) {
+            // 如果用户没有处理则让系统默认的异常处理器来处理
+            mDefaultHandler.uncaughtException(thread, ex);
+        } else {
+            try {
+                Thread.sleep(2000);
+            } catch (InterruptedException e) {
+                Log.e(TAG, "error : ", e);
+                Thread.currentThread().interrupt();
+            }
+
+//            Intent mIntent = new Intent(mContext, WelcomeActivity.class);
+//            mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+//            mContext.startActivity(mIntent);
+            android.os.Process.killProcess(android.os.Process.myPid());
+        }
+    }
+
+    /**
+     * 自定义错误处理,收集错误信息,发送错误报告等操作均在此完成
+     *
+     * @param ex
+     * @return true:如果处理了该异常信息;否则返回 false
+     */
+    private boolean handleException(Throwable ex) {
+        if (ex == null) {
+            return false;
+        }
+
+        // 使用 Toast 来显示异常信息
+        new Thread() {
+            @Override
+            public void run() {
+                Looper.prepare();
+                Toast.makeText(mContext, "好像出了点问题~~~", Toast.LENGTH_LONG).show();
+                Looper.loop();
+            }
+        }.start();
+
+        // 收集设备参数信息
+        collectDeviceInfo(mContext);
+        // 保存日志文件
+        saveCrashInfo2File(ex);
+        return true;
+    }
+
+    /**
+     * 收集设备参数信息
+     *
+     * @param ctx
+     */
+    public void collectDeviceInfo(Context ctx) {
+        try {
+            PackageManager pm = ctx.getPackageManager();
+            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
+
+            if (pi != null) {
+                String versionName = pi.versionName == null ? "null" : pi.versionName;
+                String versionCode = pi.versionCode + "";
+                infos.put("versionName", versionName);
+                infos.put("versionCode", versionCode);
+            }
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "an error occured when collect package info", e);
+        }
+
+        Field[] fields = Build.class.getDeclaredFields();
+        for (Field field : fields) {
+            try {
+                field.setAccessible(true);
+                infos.put(field.getName(), field.get(null).toString());
+                Log.d(TAG, field.getName() + " : " + field.get(null));
+            } catch (Exception e) {
+                Log.e(TAG, "an error occured when collect crash info", e);
+            }
+        }
+    }
+
+    /**
+     * 保存错误信息到文件中 *
+     *
+     * @param ex
+     * @return 返回文件名称, 便于将文件传送到服务器
+     */
+    private String saveCrashInfo2File(Throwable ex) {
+        StringBuffer sb = new StringBuffer();
+        for (Map.Entry<String, String> entry : infos.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            sb.append(String.format("%s=%s\n", key, value));
+        }
+
+        Writer writer = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(writer);
+        ex.printStackTrace(printWriter);
+        Throwable cause = ex.getCause();
+        while (cause != null) {
+            cause.printStackTrace(printWriter);
+            cause = cause.getCause();
+        }
+        printWriter.close();
+
+        String result = writer.toString();
+        sb.append(result);
+
+        long timestamp = System.currentTimeMillis();
+        String time = formatter.format(new Date());
+        String fileName = "crash-" + time + "-" + timestamp + ".log";
+
+        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+            String path = String.format("%scrash/", FileHelper.getRootFilePath());
+            File dir = new File(path);
+            if (!dir.exists()) {
+                dir.mkdirs();
+            }
+            FileOutputStream fos = null;
+            try {
+                fos = new FileOutputStream(path + fileName);
+                fos.write(sb.toString().getBytes());
+            } catch (Exception e) {
+                Log.e(TAG, "an error occured while writing file...", e);
+            } finally {
+                try {
+                    if (fos != null) {
+                        fos.close();
+                    }
+                } catch (IOException e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        return fileName;
+
+
+    }
+}

+ 17 - 0
base/src/main/java/com/xuqm/base/adapter/BaseItem.java

@@ -0,0 +1,17 @@
+package com.xuqm.base.adapter;
+
+/**
+ * 所有用到{@link com.xuqm.base.ui.BaseListActivity}来做的列表页
+ * 数据model都要继承BaseItem
+ */
+public class BaseItem {
+    private int s_id;
+
+    public int getS_id() {
+        return s_id;
+    }
+
+    public void setS_id(int s_id) {
+        this.s_id = s_id;
+    }
+}

+ 186 - 0
base/src/main/java/com/xuqm/base/adapter/BaseNormalAdapter.java

@@ -0,0 +1,186 @@
+package com.xuqm.base.adapter;
+
+import android.content.Context;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.xuqm.base.adapter.callback.AdapterItemClickListener;
+import com.xuqm.base.adapter.callback.AdapterItemLongClickListener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 不用{@link BasePagedAdapter}的时候,可以用这个
+ * <p>
+ *
+ * @param <T> 数据各式
+ */
+public abstract class BaseNormalAdapter<T> extends RecyclerView.Adapter<ViewHolder> {
+    private Context context;
+    private AdapterItemClickListener<T> itemClickListener;//item的点击事件
+    private AdapterItemLongClickListener<T> itemLongClickListener;//item的长按事件
+    private ItemViewDelegateManager<T> mItemViewDelegateManager;//ItemViewDelegate的管理类
+
+    private List<T> list;
+
+    private AdapterItemClickListener<T> listener;
+
+    public BaseNormalAdapter() {
+        this.list = new ArrayList<>();
+        mItemViewDelegateManager = new ItemViewDelegateManager<>();
+    }
+
+    public BaseNormalAdapter(List<T> list) {
+        this.list = null == list ? new ArrayList<>() : list;
+        mItemViewDelegateManager = new ItemViewDelegateManager<>();
+    }
+
+    public void setmDatas(List<T> mDatas) {
+        this.list.clear();
+        this.addmDatas(null == mDatas ? new ArrayList<>() : mDatas);
+    }
+
+    public List<T> getDatas() {
+        return this.list;
+    }
+
+    public void addmDatas(List<T> mDatas) {
+        this.list.addAll(mDatas);
+        notifyDataSetChanged();
+    }
+
+    public void addItem(T item) {
+        this.list.add(item);
+        notifyDataSetChanged();
+    }
+
+    public void removeItem(T item) {
+        this.list.remove(item);
+        notifyDataSetChanged();
+    }
+
+
+    @Override
+    public int getItemViewType(int position) {
+        if (!useItemViewDelegateManager()) return super.getItemViewType(position);
+        return mItemViewDelegateManager.getItemViewType(list.get(position), position);
+    }
+
+
+    /**
+     * 判断是否有多种ItemViewType
+     * 根据mItemViewDelegateManager 里面存储的数量决定
+     *
+     * @return true 有多种ItemViewType
+     */
+    private boolean useItemViewDelegateManager() {
+        return mItemViewDelegateManager.getItemViewDelegateCount() > 0;
+    }
+
+    /**
+     * 添加不同的item样式
+     *
+     * @param itemViewDelegate 自定义的item
+     * @return this
+     */
+    public BaseNormalAdapter addItemViewDelegate(ItemViewDelegate<T> itemViewDelegate) {
+        mItemViewDelegateManager.addDelegate(itemViewDelegate);
+        return this;
+    }
+
+    /**
+     * 添加不同的item样式
+     *
+     * @param viewType         自定义的item type 不能重复
+     * @param itemViewDelegate 自定义的item
+     * @return this
+     */
+    public BaseNormalAdapter addItemViewDelegate(int viewType, ItemViewDelegate<T> itemViewDelegate) {
+        mItemViewDelegateManager.addDelegate(viewType, itemViewDelegate);
+        return this;
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        ItemViewDelegate itemViewDelegate = mItemViewDelegateManager.getItemViewDelegate(viewType);
+        int layoutId = itemViewDelegate.getItemViewLayoutId();//这里拿到自定义的layoutId
+        context = parent.getContext();//context没用传递过来,这里自己获取到
+        return new ViewHolder(context, parent, layoutId);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+        if (null != itemClickListener) {
+            holder.itemView.setOnClickListener(v -> itemClickListener.onClick(holder.itemView, list.get(position), position));
+        }
+        if (null != itemLongClickListener) {
+            holder.itemView.setOnLongClickListener(v -> itemLongClickListener.onClick(holder.itemView, list.get(position), position));
+        }
+        convert(holder, list.get(position));
+    }
+
+    @Override
+    public int getItemCount() {
+        return list.size();
+    }
+
+    /**
+     * 设置item点击监听
+     *
+     * @param itemClickListener item的点击事件
+     */
+    public void setItemClickListener(AdapterItemClickListener<T> itemClickListener) {
+        this.itemClickListener = itemClickListener;
+    }
+
+    /**
+     * 设置item长按监听
+     *
+     * @param itemLongClickListener item的长按事件
+     */
+    public void setItemLongClickListener(AdapterItemLongClickListener<T> itemLongClickListener) {
+        this.itemLongClickListener = itemLongClickListener;
+    }
+
+    /**
+     * 部分情况可以需要用到这个,比如item里面元素想要和item使用同一个回调处理
+     *
+     * @return
+     */
+    protected AdapterItemClickListener<T> getItemClickListener() {
+        return itemClickListener;
+    }
+
+    /**
+     * ui绘制的事件,分发给ItemViewDelegate自己处理
+     * 比如settext()   setOnClickListener()这些
+     *
+     * @param holder holder
+     * @param item   item
+     */
+    public void convert(ViewHolder holder, T item) {
+        mItemViewDelegateManager.convert(holder, item, holder.getAdapterPosition());
+    }
+
+    /**
+     * 刷新知道item
+     *
+     * @param position position
+     */
+    public void changeItem(int position) {
+        if (0 <= position && position < getItemCount()) {
+            notifyItemChanged(position);
+        }
+    }
+
+    public void changeItem(int position, Object payload) {
+        if (0 <= position && position < getItemCount()) {
+            notifyItemChanged(position, payload);
+        }
+    }
+
+}

+ 157 - 0
base/src/main/java/com/xuqm/base/adapter/BasePagedAdapter.java

@@ -0,0 +1,157 @@
+package com.xuqm.base.adapter;
+
+import android.content.Context;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+import androidx.paging.PagedListAdapter;
+
+import com.xuqm.base.adapter.callback.AdapterItemClickListener;
+import com.xuqm.base.adapter.callback.AdapterItemLongClickListener;
+
+import java.util.List;
+
+/**
+ * 如果用到了{@link com.xuqm.base.ui.BaseListActivity}来展示列表页面的话,需要adapter继承这个
+ * <p>
+ * 如果item只有一种类型,可以使用{@link CommonPagedAdapter}来展示
+ * <p>
+ * 如果不用{@link CommonPagedAdapter}的话,继承后需要使用{@link #addItemViewDelegate(ItemViewDelegate)}
+ * 来设置展示的页面
+ *
+ * @param <T>
+ */
+public class BasePagedAdapter<T extends BaseItem> extends PagedListAdapter<T, ViewHolder> {
+    private Context context;
+    private AdapterItemClickListener<T> itemClickListener;//item的点击事件
+    private AdapterItemLongClickListener<T> itemLongClickListener;//item的长按事件
+    private ItemViewDelegateManager<T> mItemViewDelegateManager;//ItemViewDelegate的管理类
+
+    public BasePagedAdapter() {
+        super(new Diff<>());
+        mItemViewDelegateManager = new ItemViewDelegateManager<>();
+    }
+
+
+    @Override
+    public int getItemViewType(int position) {
+        if (!useItemViewDelegateManager()) return super.getItemViewType(position);
+        return mItemViewDelegateManager.getItemViewType(getItem(position), position);
+    }
+
+
+    /**
+     * 判断是否有多种ItemViewType
+     * 根据mItemViewDelegateManager 里面存储的数量决定
+     *
+     * @return true 有多种ItemViewType
+     */
+    private boolean useItemViewDelegateManager() {
+        return mItemViewDelegateManager.getItemViewDelegateCount() > 0;
+    }
+
+    /**
+     * 添加不同的item样式
+     *
+     * @param itemViewDelegate 自定义的item
+     * @return this
+     */
+    public BasePagedAdapter addItemViewDelegate(ItemViewDelegate<T> itemViewDelegate) {
+        mItemViewDelegateManager.addDelegate(itemViewDelegate);
+        return this;
+    }
+
+    /**
+     * 添加不同的item样式
+     *
+     * @param viewType         自定义的item type 不能重复
+     * @param itemViewDelegate 自定义的item
+     * @return this
+     */
+    public BasePagedAdapter addItemViewDelegate(int viewType, ItemViewDelegate<T> itemViewDelegate) {
+        mItemViewDelegateManager.addDelegate(viewType, itemViewDelegate);
+        return this;
+    }
+
+    @NonNull
+    @Override
+    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+        ItemViewDelegate itemViewDelegate = mItemViewDelegateManager.getItemViewDelegate(viewType);
+        int layoutId = itemViewDelegate.getItemViewLayoutId();//这里拿到自定义的layoutId
+        context = parent.getContext();//context没用传递过来,这里自己获取到
+        return new ViewHolder(context, parent, layoutId);
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+        convert(holder, getItem(position));
+    }
+
+    @Override
+    public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {
+        if (null != itemClickListener) {
+            holder.itemView.setOnClickListener(v -> itemClickListener.onClick(holder.itemView, getItem(position), position));
+        }
+        if (null != itemLongClickListener) {
+            holder.itemView.setOnLongClickListener(v -> itemLongClickListener.onClick(holder.itemView, getItem(position), position));
+        }
+        bindViewHolder(holder, getItem(position), position, payloads);
+    }
+
+    private void bindViewHolder(ViewHolder holder, T item, int position, List<Object> payloads) {
+        convert(holder, item);
+    }
+
+    /**
+     * 设置item点击监听
+     *
+     * @param itemClickListener item的点击事件
+     */
+    public void setItemClickListener(AdapterItemClickListener<T> itemClickListener) {
+        this.itemClickListener = itemClickListener;
+    }
+
+    /**
+     * 设置item长按监听
+     *
+     * @param itemLongClickListener item的长按事件
+     */
+    public void setItemLongClickListener(AdapterItemLongClickListener<T> itemLongClickListener) {
+        this.itemLongClickListener = itemLongClickListener;
+    }
+
+    /**
+     * 部分情况可以需要用到这个,比如item里面元素想要和item使用同一个回调处理
+     * @return
+     */
+    protected AdapterItemClickListener<T> getItemClickListener() {
+        return itemClickListener;
+    }
+
+    /**
+     * ui绘制的事件,分发给ItemViewDelegate自己处理
+     * 比如settext()   setOnClickListener()这些
+     * @param holder holder
+     * @param item item
+     */
+    public void convert(ViewHolder holder, T item) {
+        mItemViewDelegateManager.convert(holder, item, holder.getAdapterPosition());
+    }
+
+    /**
+     * 刷新知道item
+     * @param position position
+     */
+    public void changeItem(int position) {
+        if (0 <= position && position < getItemCount()) {
+            notifyItemChanged(position);
+        }
+    }
+
+    public void changeItem(int position, Object payload) {
+        if (0 <= position && position < getItemCount()) {
+            notifyItemChanged(position, payload);
+        }
+    }
+
+}

+ 59 - 0
base/src/main/java/com/xuqm/base/adapter/CommonAdapter.java

@@ -0,0 +1,59 @@
+package com.xuqm.base.adapter;
+
+import java.util.List;
+
+/**
+ * 这个adapter主要是用来简化通用adapter
+ * 如果item只有一种样式,或者说不需要用到itemViewType,可以直接使用这个
+ * <p>
+ * 构造函数直接传入对应的layoutId,然后重写convert方法就可以了
+ * list不传的话,后面使用{@link #setmDatas(List)} 添加就可以了
+ *
+ * @param <T> item用到的数据类型
+ */
+public abstract class CommonAdapter<T> extends BaseNormalAdapter<T> {
+
+
+    protected CommonAdapter(final int layoutId) {
+        super();
+        addItemViewDelegate(new ItemViewDelegate<T>() {
+            @Override
+            public int getItemViewLayoutId() {
+                return layoutId;
+            }
+
+            @Override
+            public boolean isForViewType(T item, int position) {
+                return true;
+            }
+
+            @Override
+            public void convert(ViewHolder holder, T t, int position) {
+                CommonAdapter.this.convert(holder, t, position);
+            }
+        });
+    }
+
+    protected CommonAdapter(final int layoutId, List<T> list) {
+        super(list);
+        addItemViewDelegate(new ItemViewDelegate<T>() {
+            @Override
+            public int getItemViewLayoutId() {
+                return layoutId;
+            }
+
+            @Override
+            public boolean isForViewType(T item, int position) {
+                return true;
+            }
+
+            @Override
+            public void convert(ViewHolder holder, T t, int position) {
+                CommonAdapter.this.convert(holder, t, position);
+            }
+        });
+    }
+
+    protected abstract void convert(ViewHolder holder, T item, int position);
+
+}

+ 36 - 0
base/src/main/java/com/xuqm/base/adapter/CommonPagedAdapter.java

@@ -0,0 +1,36 @@
+package com.xuqm.base.adapter;
+
+/**
+ * 这个adapter主要是用来简化通用列表页的绘制
+ * 如果item只有一种样式,或者说不需要用到itemViewType,可以直接使用这个
+ * <p>
+ * 构造函数直接传入对应的layoutId,然后重写convert方法就可以了
+ *
+ * @param <T> item用到的数据类型
+ */
+public abstract class CommonPagedAdapter<T extends BaseItem> extends BasePagedAdapter<T> {
+
+
+    protected CommonPagedAdapter(final int layoutId) {
+        super();
+        addItemViewDelegate(new ItemViewDelegate<T>() {
+            @Override
+            public int getItemViewLayoutId() {
+                return layoutId;
+            }
+
+            @Override
+            public boolean isForViewType(T item, int position) {
+                return true;
+            }
+
+            @Override
+            public void convert(ViewHolder holder, T t, int position) {
+                CommonPagedAdapter.this.convert(holder, t, position);
+            }
+        });
+    }
+
+    protected abstract void convert(ViewHolder holder, T item, int position);
+
+}

+ 16 - 0
base/src/main/java/com/xuqm/base/adapter/Diff.java

@@ -0,0 +1,16 @@
+package com.xuqm.base.adapter;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.DiffUtil;
+
+public class Diff<T extends BaseItem> extends DiffUtil.ItemCallback<T> {
+    @Override
+    public boolean areItemsTheSame(@NonNull T oldItem, @NonNull T newItem) {
+        return oldItem.getS_id() == newItem.getS_id();
+    }
+
+    @Override
+    public boolean areContentsTheSame(@NonNull T oldItem, @NonNull T newItem) {
+        return oldItem.getS_id() == newItem.getS_id();
+    }
+}

+ 72 - 0
base/src/main/java/com/xuqm/base/adapter/ElasticHorizontalScrollView.java

@@ -0,0 +1,72 @@
+package com.xuqm.base.adapter;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.view.MotionEvent;
+import android.widget.HorizontalScrollView;
+
+public class ElasticHorizontalScrollView extends HorizontalScrollView {
+    private float x;
+    private DisplayMetrics metrics;
+    private int threshold = 0;
+
+    public ElasticHorizontalScrollView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        metrics = getResources().getDisplayMetrics();
+    }
+
+    public ElasticHorizontalScrollView(Context context) {
+        this(context, null);
+    }
+
+    public void setThreshold(int threshold) {
+        this.threshold = threshold;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (ev == null) {
+            return super.onTouchEvent(ev);
+        } else {
+            return commOnTouchEvent(ev);
+        }
+    }
+
+    public void reset() {
+        scrollTo(0, 0);
+    }
+
+    private boolean commOnTouchEvent(MotionEvent ev) {
+        int action = ev.getAction();
+        int length = threshold;
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                x = ev.getX();
+                break;
+            case MotionEvent.ACTION_UP:
+                //复原位置
+                if ((ev.getX() - x) > 0) {
+                    if (getScrollX() > length / 2) {
+                        smoothScrollTo(length, 0);
+                    } else {
+                        smoothScrollTo(0, 0);
+                    }
+                } else {
+                    if (getScrollX() > length / 2) {
+                        smoothScrollTo(length, 0);
+                    } else {
+                        smoothScrollTo(0, 0);
+                    }
+                }
+                return true;
+            case MotionEvent.ACTION_MOVE:
+                return super.onTouchEvent(ev);
+            default:
+                return true;
+
+        }
+        return true;
+    }
+}

+ 30 - 0
base/src/main/java/com/xuqm/base/adapter/FragmentAdapter.java

@@ -0,0 +1,30 @@
+package com.xuqm.base.adapter;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.lifecycle.Lifecycle;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+
+import java.util.List;
+
+public class FragmentAdapter extends FragmentStateAdapter {
+
+    private List<Fragment> fragments;
+
+    public FragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, List<Fragment> fragments) {
+        super(fragmentManager, lifecycle);
+        this.fragments = fragments;
+    }
+
+    @NonNull
+    @Override
+    public Fragment createFragment(int position) {
+        return fragments.get(position);
+    }
+
+    @Override
+    public int getItemCount() {
+        return fragments.size();
+    }
+}

+ 15 - 0
base/src/main/java/com/xuqm/base/adapter/ItemViewDelegate.java

@@ -0,0 +1,15 @@
+package com.xuqm.base.adapter;
+
+
+/**
+ * Created by zhy on 16/6/22.
+ */
+public interface ItemViewDelegate<T> {
+
+    int getItemViewLayoutId();//这个  ItemViewDelegate  将要展示的页面
+
+    boolean isForViewType(T item, int position); //条件判断,用来判断什么时候展示这个ItemViewDelegate
+
+    void convert(ViewHolder holder, T item, int position);//UI绘制与事件添加
+
+}

+ 95 - 0
base/src/main/java/com/xuqm/base/adapter/ItemViewDelegateManager.java

@@ -0,0 +1,95 @@
+package com.xuqm.base.adapter;
+
+
+import androidx.collection.SparseArrayCompat;
+
+/**
+ * Created by zhy on 16/6/22.
+ */
+public class ItemViewDelegateManager<T> {
+    private SparseArrayCompat<ItemViewDelegate<T>> delegates = new SparseArrayCompat<>();
+
+    public int getItemViewDelegateCount() {
+        return delegates.size();
+    }
+
+    public ItemViewDelegateManager<T> addDelegate(ItemViewDelegate<T> delegate) {
+        int viewType = delegates.size();
+        if (delegate != null) {
+            delegates.put(viewType, delegate);
+        }
+        return this;
+    }
+
+    public ItemViewDelegateManager<T> addDelegate(int viewType, ItemViewDelegate<T> delegate) {
+        if (delegates.get(viewType) != null) {
+            throw new IllegalArgumentException(
+                    "An ItemViewDelegate is already registered for the viewType = "
+                            + viewType
+                            + ". Already registered ItemViewDelegate is "
+                            + delegates.get(viewType));
+        }
+        delegates.put(viewType, delegate);
+        return this;
+    }
+
+    public ItemViewDelegateManager<T> removeDelegate(ItemViewDelegate<T> delegate) {
+        if (delegate == null) {
+            throw new NullPointerException("ItemViewDelegate is null");
+        }
+        int indexToRemove = delegates.indexOfValue(delegate);
+
+        if (indexToRemove >= 0) {
+            delegates.removeAt(indexToRemove);
+        }
+        return this;
+    }
+
+    public ItemViewDelegateManager<T> removeDelegate(int itemType) {
+        int indexToRemove = delegates.indexOfKey(itemType);
+
+        if (indexToRemove >= 0) {
+            delegates.removeAt(indexToRemove);
+        }
+        return this;
+    }
+
+    int getItemViewType(T item, int position) {
+        int delegatesCount = delegates.size();
+        for (int i = 0; i < delegatesCount; i++) {
+            ItemViewDelegate<T> delegate = delegates.valueAt(i);
+            if (delegate.isForViewType(item, position)) {
+                return delegates.keyAt(i);
+            }
+        }
+        throw new IllegalArgumentException(
+                "No ItemViewDelegate added that matches position=" + position + " in data source");
+    }
+
+    void convert(ViewHolder holder, T item, int position) {
+        int delegatesCount = delegates.size();
+        for (int i = 0; i < delegatesCount; i++) {
+            ItemViewDelegate<T> delegate = delegates.valueAt(i);
+
+            if (delegate.isForViewType(item, position)) {
+                delegate.convert(holder, item, position);
+                return;
+            }
+        }
+        throw new IllegalArgumentException(
+                "No ItemViewDelegateManager added that matches position=" + position + " in data source");
+    }
+
+
+    public ItemViewDelegate getItemViewDelegate(int viewType) {
+        return delegates.get(viewType);
+    }
+
+    public int getItemViewLayoutId(int viewType) {
+        return getItemViewDelegate(viewType).getItemViewLayoutId();
+    }
+
+    public int getItemViewType(ItemViewDelegate<T> itemViewDelegate) {
+        return delegates.indexOfValue(itemViewDelegate);
+    }
+}

+ 178 - 0
base/src/main/java/com/xuqm/base/adapter/SlipReAdapter.java

@@ -0,0 +1,178 @@
+package com.xuqm.base.adapter;
+
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.LinearLayout.LayoutParams;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.xuqm.base.R;
+
+
+/**
+ * @author jose.han
+ * @date 2019/7/19 0019
+ * @description 包装器,再原有的adpter 基础上,分装测滑功能,基于
+ */
+
+public class SlipReAdapter extends RecyclerView.Adapter<SlipReAdapter.RViewHolder> {
+
+  private RecyclerView.Adapter mAdapter;
+  private ISlipClickAction mISlipClickAction;
+  private int mSlipViewId;
+  public final static int MODE_DELETE = 0;
+  public final static int MODE_CLICK = 0;
+  private int mMode = MODE_DELETE;
+  private int mSlipWidth = 0;
+
+  public static class Builder {
+
+    private RecyclerView.Adapter mAdapter;
+    private ISlipClickAction mISlipClickAction;
+    private int mSlipViewId;
+    private int mMode;
+    private int mSlipWidth;
+
+    public Builder setAdapter(RecyclerView.Adapter adapter) {
+      mAdapter = adapter;
+      return this;
+    }
+
+    public Builder setISlipClickAction(
+        ISlipClickAction ISlipClickAction) {
+      mISlipClickAction = ISlipClickAction;
+      return this;
+    }
+
+    public Builder setSlipViewId(int slipViewId) {
+      mSlipViewId = slipViewId;
+      return this;
+    }
+
+    public Builder setMode(int mode) {
+      mMode = mode;
+      return this;
+    }
+
+    public Builder setSlipWidth(float slipWidth) {
+      mSlipWidth = (int) slipWidth;
+      return this;
+    }
+
+    public SlipReAdapter build() {
+      SlipReAdapter slipReAdapter = new SlipReAdapter();
+      slipReAdapter.setAdapter(mAdapter);
+      slipReAdapter.setISlipClickAction(mISlipClickAction);
+      slipReAdapter.setMode(mMode);
+      slipReAdapter.setSlipViewId(mSlipViewId);
+      slipReAdapter.setSlipWidth(mSlipWidth);
+      return slipReAdapter;
+    }
+  }
+
+  public SlipReAdapter() {
+
+  }
+
+  public void setAdapter(RecyclerView.Adapter adapter) {
+    mAdapter = adapter;
+  }
+
+  public void setISlipClickAction(
+      ISlipClickAction ISlipClickAction) {
+    mISlipClickAction = ISlipClickAction;
+  }
+
+  public void setSlipViewId(int slipViewId) {
+    mSlipViewId = slipViewId;
+  }
+
+  public void setMode(int mode) {
+    mMode = mode;
+  }
+
+  public void setSlipWidth(int slipWidth) {
+    mSlipWidth = slipWidth;
+  }
+
+  @Override
+  public RViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+
+    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_slip, parent, false);
+    LinearLayout contentLL = view.findViewById(R.id.content_ll);
+    LinearLayout deleteLl = view.findViewById(R.id.delete_ll);
+    View delete = LayoutInflater.from(parent.getContext()).inflate(mSlipViewId, null, false);
+    deleteLl.addView(delete);
+
+    LayoutParams layoutParams = new LayoutParams(
+        parent.getResources().getDisplayMetrics().widthPixels,
+        ViewGroup.LayoutParams.WRAP_CONTENT);
+    RecyclerView.ViewHolder viewHolder = mAdapter.onCreateViewHolder(parent, viewType);
+    viewHolder.itemView.setLayoutParams(layoutParams);
+    contentLL.addView(viewHolder.itemView);
+
+    return new RViewHolder(view, viewHolder, mSlipWidth);
+  }
+
+  @Override
+  public void onBindViewHolder(final RViewHolder holder, final int position) {
+    mAdapter.onBindViewHolder(holder.mViewHolder, position);
+    holder.deleteLl.setOnClickListener(new OnClickListener() {
+      @Override
+      public void onClick(View v) {
+        mISlipClickAction.onAction(holder.getAdapterPosition());
+        holder.mElasticHorizontalScrollView.reset();
+        Log.i("SlipReAdapter", "slip action and the pos is:" + holder.getAdapterPosition());
+        if (mMode == MODE_DELETE) {
+          notifyItemRemoved(holder.getAdapterPosition());
+        } else if (mMode == MODE_CLICK) {
+          notifyItemChanged(holder.getAdapterPosition());
+        }
+      }
+    });
+  }
+
+  @Override
+  public int getItemCount() {
+    return mAdapter != null ? mAdapter.getItemCount() : 0;
+  }
+
+  public static class RViewHolder extends RecyclerView.ViewHolder {
+
+    private View deleteLl;
+    private ElasticHorizontalScrollView mElasticHorizontalScrollView;
+    private RecyclerView.ViewHolder mViewHolder;
+
+    public RViewHolder(View itemView, RecyclerView.ViewHolder viewHolder, int threshold) {
+      super(itemView);
+      mViewHolder = viewHolder;
+      deleteLl = itemView.findViewById(R.id.delete_ll);
+      mElasticHorizontalScrollView = itemView.findViewById(R.id.ElasticHorizontalScrollView);
+      if (threshold != 0) {
+        LayoutParams layoutParams = new LayoutParams(threshold,
+            ViewGroup.LayoutParams.WRAP_CONTENT);
+        deleteLl.setLayoutParams(layoutParams);
+        mElasticHorizontalScrollView.setThreshold(threshold);
+      } else {
+        deleteLl.post(new Runnable() {
+          @Override
+          public void run() {
+            int width = deleteLl.getWidth();
+            mElasticHorizontalScrollView.setThreshold(width);
+          }
+        });
+      }
+    }
+  }
+
+  public interface ISlipClickAction {
+
+    public void onAction(int position);
+  }
+
+
+}

+ 156 - 0
base/src/main/java/com/xuqm/base/adapter/ViewHolder.java

@@ -0,0 +1,156 @@
+package com.xuqm.base.adapter;
+
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Typeface;
+import android.util.SparseArray;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.ColorInt;
+import androidx.annotation.ColorRes;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.IdRes;
+import androidx.annotation.LayoutRes;
+import androidx.annotation.StringRes;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.xuqm.base.adapter.callback.AdapterClickListener;
+import com.xuqm.base.common.ImageHelper;
+
+import java.util.List;
+
+public class ViewHolder extends RecyclerView.ViewHolder {
+
+    private Context context;
+    private ViewGroup parent;
+    private int layoutId;
+
+    private SparseArray<View> views = new SparseArray<>();
+
+    public ViewHolder(Context context, ViewGroup parent, @LayoutRes int layoutId) {
+        super(LayoutInflater.from(context).inflate(layoutId, parent, false));
+        this.context = context;
+        this.parent = parent;
+        this.layoutId = layoutId;
+    }
+
+    public <T extends View> T getView(int viewId) {
+        View view = views.get(viewId);
+        if (null == view) {
+            view = itemView.findViewById(viewId);
+            if (null == view) throw new IllegalArgumentException("not found id");
+            views.put(viewId, view);
+        }
+        return (T) view;
+    }
+
+    public ViewHolder setText(@IdRes int viewId, CharSequence text) {
+        TextView textView = getView(viewId);
+        textView.setText(text);
+        return this;
+    }
+
+    public ViewHolder setTypeface(@IdRes int viewId, Typeface typeface) {
+        TextView textView = getView(viewId);
+        textView.setTypeface(typeface);
+        return this;
+    }
+
+    public ViewHolder setEnabled(@IdRes int viewId, boolean enabled) {
+        View view = getView(viewId);
+        view.setEnabled(enabled);
+        return this;
+    }
+
+    public ViewHolder setBackgroundResource(@IdRes int viewId, @DrawableRes int resId) {
+        View textView = getView(viewId);
+        textView.setBackgroundResource(resId);
+        return this;
+    }
+    public ViewHolder setBackgroundColor(@IdRes int viewId, @ColorInt int color) {
+        View textView = getView(viewId);
+        textView.setBackgroundColor(color);
+        return this;
+    }
+    public ViewHolder setTextColor(@IdRes int viewId, @ColorInt int color) {
+        TextView textView = getView(viewId);
+        textView.setTextColor(color);
+        return this;
+    }
+
+    public ViewHolder setText(@IdRes int viewId, @StringRes int resId) {
+        TextView textView = getView(viewId);
+        textView.setText(context.getString(resId));
+        return this;
+    }
+
+    public ViewHolder setImageResource(@IdRes int viewId, @DrawableRes int resId) {
+        ImageView imageView = getView(viewId);
+        imageView.setImageResource(resId);
+        return this;
+    }
+
+    public ViewHolder setImage(@IdRes int viewId, String url) {
+        ImageView imageView = getView(viewId);
+        ImageHelper.load(imageView, url);
+        return this;
+    }
+    public ViewHolder setImage(@IdRes int viewId, Bitmap  bitmap) {
+        ImageView imageView = getView(viewId);
+        ImageHelper.load(imageView, bitmap);
+        return this;
+    }
+
+    public ViewHolder gone(@IdRes int viewId) {
+        View view = getView(viewId);
+        view.setVisibility(View.GONE);
+        return this;
+    }
+    public ViewHolder invisible(@IdRes int viewId) {
+        View view = getView(viewId);
+        view.setVisibility(View.INVISIBLE);
+        return this;
+    }
+
+    public ViewHolder gone(View view) {
+        view.setVisibility(View.GONE);
+        return this;
+    }
+
+    public ViewHolder visible(@IdRes int viewId) {
+        View view = getView(viewId);
+        view.setVisibility(View.VISIBLE);
+        return this;
+    }
+
+    public ViewHolder setVisibility(@IdRes int viewId, boolean isVisible) {
+        View view = getView(viewId);
+        if (isVisible) view.setVisibility(View.VISIBLE);
+        else view.setVisibility(View.GONE);
+        return this;
+    }
+
+    public ViewHolder visible(View view) {
+        view.setVisibility(View.VISIBLE);
+        return this;
+    }
+
+    public ViewHolder setClickListener(@IdRes int viewId, AdapterClickListener adapterClickListener) {
+        View view = getView(viewId);
+        if (null != view) view.setOnClickListener(adapterClickListener::onClick);
+        return this;
+    }
+
+    public ViewHolder setClickListener(List<Integer> viewIds, AdapterClickListener adapterClickListener) {
+        for (Integer viewId : viewIds) {
+            View view = getView(viewId);
+            if (null != view) view.setOnClickListener(adapterClickListener::onClick);
+        }
+        return this;
+    }
+
+}

+ 10 - 0
base/src/main/java/com/xuqm/base/adapter/callback/AdapterClickListener.java

@@ -0,0 +1,10 @@
+package com.xuqm.base.adapter.callback;
+
+import android.view.View;
+
+/**
+ * adapter中,为item元素设置点击时间时候用到的监听
+ */
+public interface AdapterClickListener {
+    void onClick(View view);
+}

+ 11 - 0
base/src/main/java/com/xuqm/base/adapter/callback/AdapterItemClickListener.java

@@ -0,0 +1,11 @@
+package com.xuqm.base.adapter.callback;
+
+import android.view.View;
+
+/**
+ * item设置点击事件的监听
+ * @param <T>
+ */
+public interface AdapterItemClickListener<T> {
+    void onClick(View view, T item, int position);
+}

+ 11 - 0
base/src/main/java/com/xuqm/base/adapter/callback/AdapterItemLongClickListener.java

@@ -0,0 +1,11 @@
+package com.xuqm.base.adapter.callback;
+
+import android.view.View;
+
+/**
+ * item设置长按事件的监听
+ * @param <T>
+ */
+public interface AdapterItemLongClickListener<T> {
+    boolean onClick(View view, T item, int position);
+}

+ 94 - 0
base/src/main/java/com/xuqm/base/common/AppManager.java

@@ -0,0 +1,94 @@
+package com.xuqm.base.common;
+
+import android.app.Activity;
+
+import java.util.Stack;
+
+/**
+ * activity的管理栈
+ */
+public class AppManager {
+
+
+    public static AppManager getInstance() {
+        return APPHolder.INSTANCE;
+    }
+
+    private static class APPHolder {
+        private static final AppManager INSTANCE = new AppManager();
+    }
+
+    private AppManager() {
+        activityStack = new Stack<>();
+    }
+
+    private final Stack<Activity> activityStack;
+
+    //添加一个新的act
+    public void pushActivity(Activity activity) {
+        activityStack.add(activity);
+    }
+
+    /**
+     * 推出一个activity 其实toolbar的返回按钮,可以直接使用这个方法
+     *
+     * @param activity 需要退出的activity
+     */
+    public void popActivity(Activity activity) {
+        if (activityStack != null && activityStack.size() > 0) {
+            if (activity != null) {
+                activity.finish();
+                activityStack.remove(activity);
+            }
+
+        }
+    }
+
+    /**
+     * 获取当前最上面的那个activity
+     *
+     * @return
+     */
+    public Activity getActivity() {
+        return activityStack.lastElement();
+    }
+
+    /**
+     * finish最后一个
+     *
+     * @return
+     */
+    public void finish() {
+        this.popActivity(this.getActivity());
+    }
+
+
+    /**
+     * finish最后一个之外的所有页面
+     *
+     * @return
+     */
+    public void logout() {
+        if (activityStack.size() < 1)
+            return;
+        for (int i = 0; i < activityStack.size() - 1; i++) {
+            Activity activity = activityStack.firstElement();
+            if (activity == null) break;
+            popActivity(activity);
+        }
+    }
+
+    /**
+     * 退出app
+     */
+    public void exit() {
+        if (activityStack != null) {
+            while (activityStack.size() > 0) {
+                Activity activity = getActivity();
+                if (activity == null) break;
+                popActivity(activity);
+            }
+        }
+        android.os.Process.killProcess(android.os.Process.myPid());
+    }
+}

Файловите разлики са ограничени, защото са твърде много
+ 193 - 0
base/src/main/java/com/xuqm/base/common/FileHelper.java


+ 230 - 0
base/src/main/java/com/xuqm/base/common/GlideEngine.kt

@@ -0,0 +1,230 @@
+package com.xuqm.base.common
+
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.PointF
+import android.graphics.drawable.Drawable
+import android.view.View
+import android.widget.ImageView
+import androidx.annotation.NonNull
+import androidx.core.graphics.drawable.RoundedBitmapDrawable
+import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.RequestOptions
+import com.bumptech.glide.request.target.BitmapImageViewTarget
+import com.bumptech.glide.request.target.ImageViewTarget
+import com.luck.picture.lib.engine.ImageEngine
+import com.luck.picture.lib.listener.OnImageCompleteCallback
+import com.luck.picture.lib.tools.MediaUtils
+import com.luck.picture.lib.widget.longimage.ImageSource
+import com.luck.picture.lib.widget.longimage.ImageViewState
+import com.luck.picture.lib.widget.longimage.SubsamplingScaleImageView
+import com.xuqm.base.R
+import org.jetbrains.annotations.NotNull
+
+
+class GlideEngine private constructor() : ImageEngine {
+    /**
+     * 加载图片
+     *
+     * @param context
+     * @param url
+     * @param imageView
+     */
+    override fun loadImage(
+        @NotNull context: Context,
+        @NotNull url: String,
+        @NotNull imageView: ImageView
+    ) {
+        Glide.with(context)
+            .load(url)
+            .into(imageView)
+    }
+
+    /**
+     * 加载网络图片适配长图方案
+     * # 注意:此方法只有加载网络图片才会回调
+     *
+     * @param context
+     * @param url
+     * @param imageView
+     * @param longImageView
+     * @param callback      网络图片加载回调监听 {link after version 2.5.1 Please use the #OnImageCompleteCallback#}
+     */
+    override fun loadImage(
+        @NotNull context: Context, @NotNull url: String,
+        @NotNull imageView: ImageView,
+        longImageView: SubsamplingScaleImageView, callback: OnImageCompleteCallback
+    ) {
+        Glide.with(context)
+            .asBitmap()
+            .load(url)
+            .into(object : ImageViewTarget<Bitmap?>(imageView) {
+                override fun onLoadStarted(@NonNull placeholder: Drawable?) {
+                    super.onLoadStarted(placeholder)
+                    callback.onShowLoading()
+                }
+
+                override fun onLoadFailed(@NonNull errorDrawable: Drawable?) {
+                    super.onLoadFailed(errorDrawable)
+                    callback.onHideLoading()
+                }
+
+                override fun setResource(@NonNull resource: Bitmap?) {
+                    callback.onHideLoading()
+                    if (resource != null) {
+                        val eqLongImage: Boolean = MediaUtils.isLongImg(
+                            resource.width,
+                            resource.height
+                        )
+                        longImageView.visibility = if (eqLongImage) View.VISIBLE else View.GONE
+                        imageView.visibility = if (eqLongImage) View.GONE else View.VISIBLE
+                        if (eqLongImage) {
+                            // 加载长图
+                            longImageView.isQuickScaleEnabled = true
+                            longImageView.isZoomEnabled = true
+                            longImageView.isPanEnabled = true
+                            longImageView.setDoubleTapZoomDuration(100)
+                            longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP)
+                            longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER)
+                            longImageView.setImage(
+                                ImageSource.bitmap(resource),
+                                ImageViewState(0f, PointF(0f, 0f), 0)
+                            )
+                        } else {
+                            // 普通图片
+                            imageView.setImageBitmap(resource)
+                        }
+                    }
+                }
+            })
+    }
+
+    /**
+     * 加载网络图片适配长图方案
+     * # 注意:此方法只有加载网络图片才会回调
+     *
+     * @param context
+     * @param url
+     * @param imageView
+     * @param longImageView
+     * @ 已废弃
+     */
+    override fun loadImage(
+        @NotNull context: Context, @NotNull url: String,
+        @NotNull imageView: ImageView,
+        longImageView: SubsamplingScaleImageView
+    ) {
+        Glide.with(context)
+            .asBitmap()
+            .load(url)
+            .into(object : ImageViewTarget<Bitmap?>(imageView) {
+                override fun setResource(@NonNull resource: Bitmap?) {
+                    if (resource != null) {
+                        val eqLongImage: Boolean = MediaUtils.isLongImg(
+                            resource.width,
+                            resource.height
+                        )
+                        longImageView.visibility = if (eqLongImage) View.VISIBLE else View.GONE
+                        imageView.visibility = if (eqLongImage) View.GONE else View.VISIBLE
+                        if (eqLongImage) {
+                            // 加载长图
+                            longImageView.isQuickScaleEnabled = true
+                            longImageView.isZoomEnabled = true
+                            longImageView.isPanEnabled = true
+                            longImageView.setDoubleTapZoomDuration(100)
+                            longImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CENTER_CROP)
+                            longImageView.setDoubleTapZoomDpi(SubsamplingScaleImageView.ZOOM_FOCUS_CENTER)
+                            longImageView.setImage(
+                                ImageSource.bitmap(resource),
+                                ImageViewState(0f, PointF(0f, 0f), 0)
+                            )
+                        } else {
+                            // 普通图片
+                            imageView.setImageBitmap(resource)
+                        }
+                    }
+                }
+            })
+    }
+
+    /**
+     * 加载相册目录
+     *
+     * @param context   上下文
+     * @param url       图片路径
+     * @param imageView 承载图片ImageView
+     */
+    override fun loadFolderImage(
+        @NotNull context: Context,
+        @NotNull url: String,
+        @NotNull imageView: ImageView
+    ) {
+        Glide.with(context)
+            .asBitmap()
+            .load(url)
+            .override(180, 180)
+            .centerCrop()
+            .sizeMultiplier(0.5f)
+            .apply(RequestOptions().placeholder(R.drawable.picture_image_placeholder))
+            .into(object : BitmapImageViewTarget(imageView) {
+                override fun setResource(resource: Bitmap?) {
+                    val circularBitmapDrawable: RoundedBitmapDrawable =
+                        RoundedBitmapDrawableFactory.create(context.resources, resource)
+                    circularBitmapDrawable.cornerRadius = 8f
+                    imageView.setImageDrawable(circularBitmapDrawable)
+                }
+            })
+    }
+
+    /**
+     * 加载gif
+     *
+     * @param context   上下文
+     * @param url       图片路径
+     * @param imageView 承载图片ImageView
+     */
+    override fun loadAsGifImage(
+        @NotNull context: Context, @NotNull url: String,
+        @NotNull imageView: ImageView
+    ) {
+        Glide.with(context)
+            .asGif()
+            .load(url)
+            .into(imageView)
+    }
+
+    /**
+     * 加载图片列表图片
+     *
+     * @param context   上下文
+     * @param url       图片路径
+     * @param imageView 承载图片ImageView
+     */
+    override fun loadGridImage(
+        @NotNull context: Context,
+        @NotNull url: String,
+        @NotNull imageView: ImageView
+    ) {
+        Glide.with(context)
+            .load(url)
+            .override(200, 200)
+            .centerCrop()
+            .apply(RequestOptions().placeholder(R.drawable.picture_image_placeholder))
+            .into(imageView)
+    }
+
+    companion object {
+        private var instance: GlideEngine? = null
+        fun createGlideEngine(): GlideEngine? {
+            if (null == instance) {
+                synchronized(GlideEngine::class.java) {
+                    if (null == instance) {
+                        instance = GlideEngine()
+                    }
+                }
+            }
+            return instance
+        }
+    }
+}

+ 54 - 0
base/src/main/java/com/xuqm/base/common/GsonImplHelp.java

@@ -0,0 +1,54 @@
+package com.xuqm.base.common;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Created by xuqm on 2016/6/3.
+ */
+public class GsonImplHelp extends Json {
+    private Gson gson = new Gson();
+
+    @Override
+    public String toJson(Object src) {
+        return gson.toJson(src);
+
+    }
+
+    @Override
+    public <T> T toObject(String json, Class<T> claxx) {
+        return gson.fromJson(json, claxx);
+
+    }
+
+    @Override
+    public <T> T toObject(byte[] bytes, Class<T> claxx) {
+        return gson.fromJson(new String(bytes), claxx);
+
+    }
+
+    public <T> List<T> toList(String json, Class<T> clazz) {
+        JsonArray jsonArray = new JsonParser().parse(json).getAsJsonArray();
+
+        List<T> list = new ArrayList<>();
+        for (JsonElement jsonElement : jsonArray) {
+            list.add(gson.fromJson(jsonElement, clazz)); //cls
+        }
+
+        return list;
+
+
+    }
+
+    public static <T> List<T> stringToArray(String s, Class<T[]> cls) {
+        T[] array = new Gson().fromJson(s, cls);
+        return Arrays.asList(array);
+    }
+
+}

+ 65 - 0
base/src/main/java/com/xuqm/base/common/ImageHelp.java

@@ -0,0 +1,65 @@
+package com.xuqm.base.common;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.util.Base64;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/***************************************************************************
+ * <pre></pre>
+ * @文件名称: ImageHelp
+ * @包 路   径:  com.xuqm.base.common
+ * @版权所有:北京数字医信责任有限公司 (C) 2021
+ *
+ * @类描述:
+ * @版本: V1.0
+ * @创建人: xuqm
+ * @创建时间:2021/9/26 下午 06:11
+ * @修改记录:
+ */
+public class ImageHelp {
+    /*
+     * bitmap转base64
+     * */
+    public static String bitmapToBase64(Bitmap bitmap) {
+        String result = null;
+        ByteArrayOutputStream baos = null;
+        try {
+            if (bitmap != null) {
+                baos = new ByteArrayOutputStream();
+                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
+
+                baos.flush();
+                baos.close();
+
+                byte[] bitmapBytes = baos.toByteArray();
+                result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                if (baos != null) {
+                    baos.flush();
+                    baos.close();
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+        }
+        return result;
+    }
+
+    /**
+     * base64转为bitmap
+     *
+     * @param base64Data
+     * @return
+     */
+    public static Bitmap base64ToBitmap(String base64Data) {
+        byte[] bytes = Base64.decode(base64Data, Base64.DEFAULT);
+        return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+    }
+}

+ 20 - 0
base/src/main/java/com/xuqm/base/common/ImageHelper.java

@@ -0,0 +1,20 @@
+package com.xuqm.base.common;
+
+import android.widget.ImageView;
+
+import com.bumptech.glide.Glide;
+
+/**
+ * 一个image相关的工具类
+ */
+public class ImageHelper {
+    /**
+     * 给imageView添加图片的方法
+     *
+     * @param imageView 需要添加图片的控件
+     * @param url       url地址,可以是path  draw等
+     */
+    public static void load(ImageView imageView, Object url) {
+        Glide.with(imageView).load(url).into(imageView);
+    }
+}

+ 29 - 0
base/src/main/java/com/xuqm/base/common/Json.java

@@ -0,0 +1,29 @@
+package com.xuqm.base.common;
+
+import java.util.List;
+
+/**
+ * Created by xuqm on 2016/6/3.
+ */
+public abstract class Json {
+    private static Json json;
+
+    Json() {
+    }
+
+    public static Json get() {
+        if (json == null) {
+            json = new GsonImplHelp();
+        }
+        return json;
+    }
+
+    public abstract String toJson(Object src);
+
+    public abstract <T> T toObject(String json, Class<T> claxx);
+
+    public abstract <T> T toObject(byte[] bytes, Class<T> claxx);
+
+    public abstract <T> List<T> toList(String json, Class<T> claxx);
+
+}

+ 75 - 0
base/src/main/java/com/xuqm/base/common/LogHelper.java

@@ -0,0 +1,75 @@
+package com.xuqm.base.common;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.orhanobut.logger.Logger;
+
+import java.util.Locale;
+
+/**
+ * 日志库用的是 KLog
+ * 平常自己调试时候不想用那么多,就随便写了个类
+ */
+public class LogHelper {
+
+
+    public static void d(String tag, Object object) {
+        Logger.t(tag).d(object);
+    }
+
+    public static void d(Object object) {
+        StackTraceElement caller = getCallerStackTraceElement();
+        String tag = generateTag(caller);
+        Logger.t(tag).d(object);
+    }
+
+    public static void d(@NonNull String message, @Nullable Object... args) {
+        Logger.t(message).d(args);
+    }
+
+    public static void e(String tag, Object object) {
+        Logger.t(tag).e(object.toString());
+    }
+
+    public static void e(Object object) {
+        if (null == object){
+            return;
+        }
+        StackTraceElement caller = getCallerStackTraceElement();
+        String tag = generateTag(caller);
+        Logger.t(tag).e("=====>" + object.toString());
+    }
+
+    public static void e(String msg, Throwable tr) {
+        StackTraceElement caller = getCallerStackTraceElement();
+        String tag = generateTag(caller);
+        Logger.t(tag).e(tr, msg);
+    }
+
+    public static void e(String tag, String msg, Throwable tr) {
+        Logger.t(tag).e(tr, msg);
+    }
+
+    public static void json(String msg) {
+        StackTraceElement caller = getCallerStackTraceElement();
+        String tag = generateTag(caller);
+        Logger.t(tag).json(msg);
+    }
+
+    public static void json(String tag, String msg) {
+        Logger.t(tag).json(msg);
+    }
+
+
+    private static String generateTag(StackTraceElement caller) {
+        String tag = "%s.%s(L:%d)";
+        String callerClazzName = caller.getClassName();
+        callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1);
+        return String.format(Locale.getDefault(), tag, callerClazzName, caller.getMethodName(), caller.getLineNumber());
+    }
+
+    private static StackTraceElement getCallerStackTraceElement() {
+        return Thread.currentThread().getStackTrace()[4];
+    }
+}

+ 8 - 0
base/src/main/java/com/xuqm/base/common/RefreshResult.java

@@ -0,0 +1,8 @@
+package com.xuqm.base.common;
+
+/**
+ * 下拉刷新的状态码表
+ */
+public enum RefreshResult {
+    SUCCEED, FAILED, NO_DATA, NO_MORE
+}

Някои файлове не бяха показани, защото твърде много файлове са промени