Przeglądaj źródła

Merge branch 'feture/pulse' into develop

cc12458 1 miesiąc temu
rodzic
commit
474d75d4a0
26 zmienionych plików z 479 dodań i 36 usunięć
  1. 2 0
      .idea/gradle.xml
  2. 1 0
      app/build.gradle.kts
  3. 5 0
      app/src/main/java/com/hzliuzhi/applet/container/MainActivity.kt
  4. 6 0
      app/src/main/res/values/application.xml
  5. 0 4
      app/src/main/res/values/route.xml
  6. 0 3
      app/src/main/res/values/strings.xml
  7. 0 17
      core/src/main/java/com/hzliuzhi/applet/core/SharedFlowHub.kt
  8. 1 0
      gradle.properties
  9. 1 1
      library/browser/src/main/java/com/hzliuzhi/applet/browser/request/Client.kt
  10. 32 11
      library/browser/src/main/java/com/hzliuzhi/applet/browser/webview/WebViewBridge.kt
  11. 1 0
      library/device/pulse/.gitignore
  12. 58 0
      library/device/pulse/build.gradle.kts
  13. 0 0
      library/device/pulse/consumer-rules.pro
  14. 21 0
      library/device/pulse/proguard-rules.pro
  15. 24 0
      library/device/pulse/src/androidTest/java/com/hzliuzhi/applet/device/pulse/ExampleInstrumentedTest.kt
  16. 4 0
      library/device/pulse/src/main/AndroidManifest.xml
  17. 58 0
      library/device/pulse/src/main/java/com/hzliuzhi/applet/device/pulse/PulseEventHandler.kt
  18. 95 0
      library/device/pulse/src/main/java/com/hzliuzhi/applet/device/pulse/PulseResult.kt
  19. 36 0
      library/device/pulse/src/main/java/com/hzliuzhi/applet/device/pulse/util/TaiYiResult.kt
  20. 55 0
      library/device/pulse/src/main/java/com/hzliuzhi/applet/device/pulse/util/TaiYiUtil.kt
  21. 16 0
      library/device/pulse/src/main/res/values/taiyi.xml
  22. 17 0
      library/device/pulse/src/test/java/com/hzliuzhi/applet/device/pulse/ExampleUnitTest.kt
  23. BIN
      local-repo/com/taiyi/sdk/pulse/ble/1.1.0-alpha.02/ble-1.1.0-alpha.02.aar
  24. 30 0
      local-repo/com/taiyi/sdk/pulse/ble/1.1.0-alpha.02/ble-1.1.0-alpha.02.pom
  25. 13 0
      local-repo/com/taiyi/sdk/pulse/ble/maven-metadata.xml
  26. 3 0
      settings.gradle.kts

+ 2 - 0
.idea/gradle.xml

@@ -14,6 +14,8 @@
             <option value="$PROJECT_DIR$/core" />
             <option value="$PROJECT_DIR$/library" />
             <option value="$PROJECT_DIR$/library/browser" />
+            <option value="$PROJECT_DIR$/library/device" />
+            <option value="$PROJECT_DIR$/library/device/pulse" />
           </set>
         </option>
       </GradleProjectSettings>

+ 1 - 0
app/build.gradle.kts

@@ -59,4 +59,5 @@ dependencies {
 
   implementation(project(":core"))
   implementation(project(":library:browser"))
+  implementation(project(":library:device:pulse"))
 }

+ 5 - 0
app/src/main/java/com/hzliuzhi/applet/container/MainActivity.kt

@@ -2,15 +2,18 @@ package com.hzliuzhi.applet.container
 
 import android.os.Bundle
 import androidx.activity.ComponentActivity
+import androidx.activity.compose.LocalActivity
 import androidx.activity.compose.setContent
 import androidx.activity.enableEdgeToEdge
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.rememberCoroutineScope
 import androidx.compose.ui.Modifier
 import androidx.navigation.NavHostController
 import androidx.navigation.compose.rememberNavController
 import com.hzliuzhi.applet.container.navigation.Host
 import com.hzliuzhi.applet.core.theme.SixTheme
+import com.hzliuzhi.applet.device.pulse.PulseEventHandle
 
 class MainActivity : ComponentActivity() {
   private var navController: NavHostController? = null
@@ -28,6 +31,8 @@ class MainActivity : ComponentActivity() {
           }
         }
       }
+
+      LocalActivity.current?.also { PulseEventHandle(it, rememberCoroutineScope()) }
     }
   }
 }

+ 6 - 0
app/src/main/res/values/application.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <string name="app_id">Applet</string>
+  <string name="app_name">小程序容器</string>
+  <string name="app_screen" />
+</resources>

+ 0 - 4
app/src/main/res/values/route.xml

@@ -1,4 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-  <string name="app_screen" />
-</resources>

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

@@ -1,3 +0,0 @@
-<resources>
-  <string name="app_name">Six-applet.Container</string>
-</resources>

+ 0 - 17
core/src/main/java/com/hzliuzhi/applet/core/SharedFlowHub.kt

@@ -1,17 +0,0 @@
-package com.hzliuzhi.applet.core
-
-import kotlinx.coroutines.flow.MutableSharedFlow
-import kotlinx.coroutines.flow.asSharedFlow
-
-object SharedFlowHub {
-  data class Event<T>(
-    val type: String,
-    val payload: T?,
-    val callback: (() -> Unit)? = null,
-  )
-
-  private val _events = MutableSharedFlow<Event<Any>>(extraBufferCapacity = 16)
-  val events = _events.asSharedFlow()
-
-  fun emit(event: Event<Any>) = _events.tryEmit(event)
-}

+ 1 - 0
gradle.properties

@@ -15,6 +15,7 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
 # Android operating system, and which are packaged with your app's APK
 # https://developer.android.com/topic/libraries/support-library/androidx-rn
 android.useAndroidX=true
+android.enableJetifier=true
 # Kotlin code style for this project: "official" or "obsolete":
 kotlin.code.style=official
 # Enables namespacing of each library's R class so that its R class includes only the

+ 1 - 1
library/browser/src/main/java/com/hzliuzhi/applet/browser/request/Client.kt

@@ -22,7 +22,7 @@ object Client {
   suspend fun fetchWithView(request: WebResourceRequest, url: String): WebResourceResponse? {
     Log.i("log:webview", "[${request.method}] 被拦截的加载请求: ${request.url} -> $url")
     return fetchWithView {
-      Request.Builder().url(url).apply {
+      Request.Builder().url(url).method(request.method, null).apply {
         request.requestHeaders.onEach { (key, value) ->
           addHeader(key, value)
         }

+ 32 - 11
library/browser/src/main/java/com/hzliuzhi/applet/browser/webview/WebViewBridge.kt

@@ -6,11 +6,17 @@ import android.webkit.JavascriptInterface
 import android.webkit.WebView
 import com.google.gson.Gson
 import com.google.gson.JsonElement
-import com.hzliuzhi.applet.core.SharedFlowHub
+import com.hzliuzhi.applet.core.shared.Event
+import com.hzliuzhi.applet.core.shared.SharedFlowHub
+import com.hzliuzhi.applet.core.shared.SharedFlowHub.cast
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.withContext
+import org.json.JSONObject
 import java.io.InputStreamReader
 
 class WebViewBridge(private val coroutineScope: CoroutineScope) {
@@ -45,6 +51,14 @@ class WebViewBridge(private val coroutineScope: CoroutineScope) {
       }.also { lastScriptText = it }
     }.getOrNull()
     script?.also { webview.evaluateJavascript(it, null) }
+
+    SharedFlowHub.events.filter { it.type == "${SharedFlowHub.WEBVIEW_BRIDGE_EVENT}:js" }.onEach { event ->
+      Log.d("log:bridge", "发送消息事件: $event")
+      event.cast<Message, String>()?.also {
+        val payload = Gson().toJson(it.payload)
+        webview.evaluateJavascript("Bridge.getInstance().dispatch(${JSONObject.quote(payload)})", it.callback)
+      }
+    }.launchIn(coroutineScope)
   }
 
 
@@ -53,22 +67,29 @@ class WebViewBridge(private val coroutineScope: CoroutineScope) {
   @SuppressLint("JavascriptInterface")
   internal suspend fun WebView.handleBridge(): Nothing = withContext(Dispatchers.Main) {
     addJavascriptInterface(this@WebViewBridge, "AndroidBridge")
-    messages.collect {
-      Log.d("log:bridge", "接收到的事件: ${it.type}")
-      SharedFlowHub.emit(
-        SharedFlowHub.Event(
-        type = it.type,
-        payload = it.payload,
-        callback = {}
-      ))
-    }
+    messages.collect { SharedFlowHub.webViewEmit(it) }
   }
 
+  private fun SharedFlowHub.webViewEmit(message: Message) {
+    Log.d("log:bridge", "接收消息: $message")
+    Event<JsonElement, JsonElement>(
+      type = "$WEBVIEW_BRIDGE_EVENT:${message.type}",
+      payload = message.payload,
+      callback = { payload ->
+        Event<Message, String>(
+          type = "$WEBVIEW_BRIDGE_EVENT:js",
+          payload = message.copy(payload = payload)
+        ).also { emit(it) }
+      }
+    ).also { emit(it) }
+  }
 
+  /* 注入到 JavaScript 中的方法 */
   @JavascriptInterface
   fun postMessage(string: String) {
     Message.fromJson(string)?.also {
       messages.tryEmit(it)
     }
   }
-}
+}
+

+ 1 - 0
library/device/pulse/.gitignore

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

+ 58 - 0
library/device/pulse/build.gradle.kts

@@ -0,0 +1,58 @@
+plugins {
+  alias(libs.plugins.android.library)
+  alias(libs.plugins.kotlin.android)
+  alias(libs.plugins.kotlin.compose)
+}
+
+android {
+  namespace = "com.hzliuzhi.applet.device.pulse"
+  compileSdk = 35
+
+  defaultConfig {
+    minSdk = 26
+
+    testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+    consumerProguardFiles("consumer-rules.pro")
+  }
+
+  buildTypes {
+    release {
+      isMinifyEnabled = false
+      proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+    }
+  }
+  compileOptions {
+    sourceCompatibility = JavaVersion.VERSION_11
+    targetCompatibility = JavaVersion.VERSION_11
+  }
+  kotlinOptions {
+    jvmTarget = "11"
+  }
+  buildFeatures {
+    compose = true
+  }
+}
+
+dependencies {
+
+  implementation(libs.androidx.core.ktx)
+  implementation(libs.androidx.appcompat)
+  implementation(libs.androidx.activity.compose)
+  implementation(platform(libs.androidx.compose.bom))
+  testImplementation(libs.junit)
+  androidTestImplementation(libs.androidx.junit)
+  androidTestImplementation(libs.androidx.espresso.core)
+
+  implementation(project(":core"))
+  implementation(libs.gson)
+
+  implementation("com.taiyi.sdk.pulse:ble:1.1.0-alpha.02")
+  implementation("com.android.volley:volley:1.2.1")
+  implementation("com.opencsv:opencsv:5.6")
+  implementation("com.aliyun.dpa:oss-android-sdk:2.9.21")
+  implementation("com.jakewharton.rxbinding2:rxbinding:2.0.0")
+  implementation("io.reactivex.rxjava2:rxjava:2.1.7")
+  implementation("io.reactivex.rxjava2:rxandroid:2.0.1")
+  implementation("org.greenrobot:eventbus:3.3.1")
+  implementation("com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.41")
+}

+ 0 - 0
library/device/pulse/consumer-rules.pro


+ 21 - 0
library/device/pulse/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

+ 24 - 0
library/device/pulse/src/androidTest/java/com/hzliuzhi/applet/device/pulse/ExampleInstrumentedTest.kt

@@ -0,0 +1,24 @@
+package com.hzliuzhi.applet.device.pulse
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+import org.junit.Assert.*
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+  @Test
+  fun useAppContext() {
+    // Context of the app under test.
+    val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+    assertEquals("com.hzliuzhi.applet.device.pulse.test", appContext.packageName)
+  }
+}

+ 4 - 0
library/device/pulse/src/main/AndroidManifest.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+</manifest>

+ 58 - 0
library/device/pulse/src/main/java/com/hzliuzhi/applet/device/pulse/PulseEventHandler.kt

@@ -0,0 +1,58 @@
+package com.hzliuzhi.applet.device.pulse
+
+import android.app.Activity
+import android.util.Log
+import com.google.gson.Gson
+import com.google.gson.JsonElement
+import com.hzliuzhi.applet.core.shared.Payload
+import com.hzliuzhi.applet.core.shared.SharedFlowHub
+import com.hzliuzhi.applet.core.shared.SharedFlowHub.callbackAs
+import com.hzliuzhi.applet.core.shared.SharedFlowHub.cast
+import com.hzliuzhi.applet.device.pulse.util.TaiYiUtil
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+
+class PulseEventHandle(private val activity: Activity, scope: CoroutineScope) {
+
+  private data class Pulse(
+    val userId: String?,
+  ) {
+    companion object {
+      fun formJson(element: JsonElement): Pulse {
+        return Gson().fromJson(element, Pulse::class.java)
+      }
+    }
+  }
+
+  init {
+    TaiYiUtil.init(activity.application)
+
+    SharedFlowHub.events
+      .filter { it.type.contains("pulse") }
+      .onEach { _event ->
+        when (_event.type) {
+
+          "${SharedFlowHub.WEBVIEW_BRIDGE_EVENT}:pulse" -> {
+            _event.cast<JsonElement, JsonElement>()?.also { event ->
+              val pulse = event.payload?.let { it -> Pulse.formJson(it) }
+              val callback = event.callback ?: {}
+              handlePulse(pulse) { payload ->
+                payload.copyWith(data = payload.data?.toMap()).toEvent()?.also { callback(it) }
+              }
+            }
+          }
+
+          else -> _event.callbackAs<Payload<Unit>>()?.invoke(Payload.error(message = "[pulse] 未实现 ${_event.type}"))
+        }
+      }
+      .launchIn(scope)
+  }
+
+  private fun handlePulse(pulse: Pulse?, callback: ((Payload<PulseResult?>) -> Unit)) {
+    if (pulse == null || pulse.userId.isNullOrEmpty()) Payload.error<PulseResult?>(message = "[pulse] 参数解析错误").also { callback(it) }
+    else TaiYiUtil.start(activity, "six:${pulse.userId}", callback)
+  }
+}
+

+ 95 - 0
library/device/pulse/src/main/java/com/hzliuzhi/applet/device/pulse/PulseResult.kt

@@ -0,0 +1,95 @@
+package com.hzliuzhi.applet.device.pulse
+
+import com.google.gson.Gson
+import com.google.gson.annotations.SerializedName
+import com.google.gson.stream.JsonReader
+import java.io.StringReader
+
+
+data class PulseResult(
+  @SerializedName("summary_desc")
+  val summaryLabel: SummaryLabel? = null,
+  @SerializedName("summary")
+  val summaryValue: SummaryValue? = null,
+  val time: String? = null,
+
+  val appId: String? = null,
+  val userId: String? = null,
+  val measureId: String? = null,
+  val url: String? = null,
+  val report: String = "",
+) {
+  data class SummaryLabel(
+    @SerializedName("summary")
+    val hands: List<String>? = emptyList(),
+    val left: Detail? = null,
+    val right: Detail? = null,
+  ) {
+    data class Detail(
+      val summary: List<String>? = emptyList(),
+      val guan: String? = null,
+      val chi: String? = null,
+      val cun: String? = null,
+    )
+  }
+
+  data class SummaryValue(
+    /* 弦 */
+    val xian: List<Float>? = emptyList(),
+    /* 软 */
+    val ruan: List<Float>? = emptyList(),
+
+    /* 浮 */
+    val fu: List<Float>? = emptyList(),
+    /* 沉 */
+    val chen: List<Float>? = emptyList(),
+
+    /* 滑 */
+    val hua: List<Float>? = emptyList(),
+    /* 细 */
+    val xi: List<Float>? = emptyList(),
+
+    /* 数 */
+    val shu: List<Float>? = emptyList(),
+    /* 迟 */
+    val chi: List<Float>? = emptyList(),
+
+    val kong: List<Float>? = emptyList(),
+    val shi: List<Float>? = emptyList(),
+  )
+
+
+  companion object {
+    fun fromJson(json: String) = fromJson(JsonReader(StringReader(json)))
+
+    private fun fromJson(reader: JsonReader) = try {
+      Gson().fromJson<PulseResult>(reader, PulseResult::class.java)
+    } catch (_: Throwable) {
+      null
+    }
+
+    fun toJson(result: PulseResult): String = try {
+      toMap(result).let { Gson().toJson(it) }
+    } catch (_: Throwable) {
+      ""
+    }
+
+    fun toMap(result: PulseResult): Map<String, Any?> {
+      val map = mutableMapOf<String, Any?>()
+      map["summaryLabel"] = result.summaryLabel
+      map["summaryValue"] = result.summaryValue
+
+      map["time"] = result.time
+
+      map["appId"] = result.appId
+      map["userId"] = result.userId
+      map["measureId"] = result.measureId
+      map["url"] = result.url
+      map["report"] = result.report
+      return map
+    }
+  }
+
+  fun toMap() = toMap(this)
+  override fun toString(): String = toJson(this)
+}

+ 36 - 0
library/device/pulse/src/main/java/com/hzliuzhi/applet/device/pulse/util/TaiYiResult.kt

@@ -0,0 +1,36 @@
+package com.hzliuzhi.applet.device.pulse.util
+
+import com.google.gson.Gson
+import com.google.gson.annotations.SerializedName
+import com.hzliuzhi.applet.device.pulse.PulseResult
+
+data class TaiYiResult(
+  val measureId: String? = null,
+  @SerializedName("createTime")
+  val time: String? = null,
+  @SerializedName("pulseUrl")
+  val url: String? = null,
+  @SerializedName("handStyle")
+  val hands: String?,
+) {
+  companion object {
+    fun fromJson(json: String) = try {
+      Gson().fromJson(json, TaiYiResult::class.java)
+    } catch (_: Throwable) {
+      null
+    }
+
+    fun toResult(json: String, measureId: String, report: String): PulseResult? {
+      val result = fromJson(json)
+      return PulseResult.fromJson(report)?.let {
+        it.copy(
+          report = report,
+          url = result?.url,
+
+          time = result?.time ?: it.time,
+          measureId = result?.measureId ?: it.measureId ?: measureId
+        )
+      }
+    }
+  }
+}

+ 55 - 0
library/device/pulse/src/main/java/com/hzliuzhi/applet/device/pulse/util/TaiYiUtil.kt

@@ -0,0 +1,55 @@
+package com.hzliuzhi.applet.device.pulse.util
+
+import android.app.Activity
+import android.app.Application
+import com.hzliuzhi.applet.core.shared.Payload
+import com.hzliuzhi.applet.core.util.proxy
+import com.hzliuzhi.applet.device.pulse.PulseResult
+import com.hzliuzhi.applet.device.pulse.R
+import com.taiyi.tyusbsdk.pulse.TaiyiConfig
+import com.taiyi.tyusbsdk.pulse.TaiyiManager
+import com.taiyi.tyusbsdk.pulse.net.HttpImpl
+import com.taiyi.zhimai.ui.activity.MeasureMainActivity
+import kotlinx.coroutines.suspendCancellableCoroutine
+import kotlin.coroutines.resume
+import kotlin.coroutines.resumeWithException
+
+object TaiYiUtil {
+  fun init(application: Application) = init(application, null)
+  fun init(application: Application, config: TaiyiConfig?) = TaiyiManager.getInstance().init(
+    application,
+    config ?: application.applicationContext.let { context ->
+      TaiyiConfig.getDefault(context).apply {
+        val resources = context.resources
+        skipReport = resources.getBoolean(R.bool.taiyi_skip_report)
+        delayDisconnect = resources.getInteger(R.integer.taiyi_delay_disconnect)
+        this.proxy = resources.proxy(R.array.browser_proxy_pool)
+      }
+    },
+  )
+
+  fun start(activity: Activity, userId: String, callback: ((Payload<PulseResult?>) -> Unit)) {
+    TaiyiManager.getInstance().toMeasure(
+      userId, activity, MeasureMainActivity::class.java,
+      { callback(Payload.error(code = -10, message = "脉诊未完成")) },
+      { data, measureId, report ->
+        TaiYiResult.toResult(data, measureId, report)?.also { result -> callback(Payload.data(result, message = "脉诊已完成 ($measureId)")) }
+      }
+    )
+  }
+
+  fun getReportUrlSync(measureId: String) = TaiyiManager.getInstance().getUrl(measureId)
+  suspend fun getReportUrlUrl(measureId: String) {
+    suspendCancellableCoroutine { cont ->
+      TaiyiManager.getInstance().getAsyncUrl(measureId, object : HttpImpl<String> {
+        override fun showError(message: String?) {
+          cont.resumeWithException(Exception(message))
+        }
+
+        override fun showResponse(url: String) {
+          cont.resume(url)
+        }
+      })
+    }
+  }
+}

+ 16 - 0
library/device/pulse/src/main/res/values/taiyi.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+  <string name="taiyi_app_id">hJn5D3rr</string>
+  <string name="taiyi_app_secret">0f0d450226f316d668d6eb8bcfbb4acca4ccc47f</string>
+  <bool name="taiyi_skip_report">false</bool>
+  <integer name="taiyi_delay_disconnect">0</integer>
+
+  <string-array name="browser_proxy_pool">
+    <!-- <item>https://hybrid.reborn-tech.com -> </item> -->
+    <!-- <item>https://api.reborn-tech.com -> </item> -->
+    <!-- <item>https://taiyi.oss-accelerate.aliyuncs.com -> </item> -->
+    <!-- <item>https://taiyi.oss-cn-beijing.aliyuncs.com -> </item> -->
+    <!-- <item>https://oss-accelerate.aliyuncs.com/taiyi -> </item> -->
+    <!-- <item>https://oss-cn-beijing.aliyuncs.com/taiyi -> </item> -->
+  </string-array>
+</resources>

+ 17 - 0
library/device/pulse/src/test/java/com/hzliuzhi/applet/device/pulse/ExampleUnitTest.kt

@@ -0,0 +1,17 @@
+package com.hzliuzhi.applet.device.pulse
+
+import org.junit.Test
+
+import org.junit.Assert.*
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+class ExampleUnitTest {
+  @Test
+  fun addition_isCorrect() {
+    assertEquals(4, 2 + 2)
+  }
+}

BIN
local-repo/com/taiyi/sdk/pulse/ble/1.1.0-alpha.02/ble-1.1.0-alpha.02.aar


+ 30 - 0
local-repo/com/taiyi/sdk/pulse/ble/1.1.0-alpha.02/ble-1.1.0-alpha.02.pom

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <groupId>com.taiyi.sdk.pulse</groupId>
+  <artifactId>ble</artifactId>
+  <version>1.1.0-alpha.02</version>
+  <packaging>aar</packaging>
+  <name>ble</name>
+  <description>six.pulse:packag</description>
+  <dependencies>
+    <dependency>
+      <groupId>androidx.appcompat</groupId>
+      <artifactId>appcompat</artifactId>
+      <version>1.4.1</version>
+      <scope>implementation</scope>
+    </dependency>
+    <dependency>
+      <groupId>com.google.android.material</groupId>
+      <artifactId>material</artifactId>
+      <version>1.5.0</version>
+      <scope>implementation</scope>
+    </dependency>
+    <dependency>
+      <groupId>androidx.constraintlayout</groupId>
+      <artifactId>constraintlayout</artifactId>
+      <version>2.1.3</version>
+      <scope>implementation</scope>
+    </dependency>
+  </dependencies>
+</project>

+ 13 - 0
local-repo/com/taiyi/sdk/pulse/ble/maven-metadata.xml

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<metadata>
+  <groupId>com.taiyi.sdk.pulse</groupId>
+  <artifactId>ble</artifactId>
+  <versioning>
+    <latest>1.1.0-alpha.02</latest>
+    <release>1.1.0-alpha.02</release>
+    <versions>
+      <version>1.1.0-alpha.02</version>
+    </versions>
+    <lastUpdated>20250622165951</lastUpdated>
+  </versioning>
+</metadata>

+ 3 - 0
settings.gradle.kts

@@ -16,6 +16,8 @@ dependencyResolutionManagement {
   repositories {
     google()
     mavenCentral()
+    maven { url = uri("https://jitpack.io") }
+    maven { url = uri("${rootProject.projectDir}/local-repo") }
   }
 }
 
@@ -23,3 +25,4 @@ rootProject.name = "Six-applet.Container"
 include(":app")
 include(":core")
 include(":library:browser")
+include(":library:device:pulse")