Skip to content

Commit bca05f6

Browse files
author
huteng
committed
feat: resolve conflicts
2 parents 126fe46 + db53beb commit bca05f6

352 files changed

Lines changed: 14891 additions & 1360 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/release.yml

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,21 @@ on:
44
push:
55
tags:
66
- 'v*'
7+
workflow_dispatch:
8+
inputs:
9+
tag:
10+
description: 'Tag to release (e.g., v1.2.0)'
11+
required: true
12+
type: string
13+
skip_changelog:
14+
description: 'Skip changelog generation'
15+
required: false
16+
type: boolean
17+
default: false
718

819
jobs:
9-
release:
20+
changelog:
21+
if: github.event_name == 'push' || !inputs.skip_changelog
1022
runs-on: ubuntu-latest
1123
permissions:
1224
contents: write
@@ -19,6 +31,64 @@ jobs:
1931
with:
2032
node-version: lts/*
2133

22-
- run: npx changelogithub
34+
- name: Generate Changelog
35+
run: npx changelogithub
2336
env:
24-
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
37+
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
38+
39+
cocoapods:
40+
if: always()
41+
runs-on: macos-latest
42+
needs: changelog
43+
steps:
44+
- uses: actions/checkout@v4
45+
46+
- name: Set up Ruby
47+
uses: ruby/setup-ruby@v1
48+
with:
49+
ruby-version: '3.0'
50+
bundler-cache: true
51+
52+
- name: Extract version from tag
53+
id: version
54+
run: |
55+
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
56+
TAG="${{ github.event.inputs.tag }}"
57+
else
58+
TAG=${GITHUB_REF#refs/tags/}
59+
fi
60+
# Remove 'v' prefix if present
61+
VERSION="${TAG#v}"
62+
echo "version=$VERSION" >> $GITHUB_OUTPUT
63+
echo "Tag: $TAG, Extracted version: $VERSION"
64+
65+
- name: Verify podspec version
66+
run: |
67+
PODSPEC_VERSION=$(grep "^\s*s\.version" Dimina.podspec | sed -E "s/.*'(.*)'.*/\1/" | tr -d '[:space:]')
68+
TAG_VERSION="${{ steps.version.outputs.version }}"
69+
TAG_VERSION=$(echo "$TAG_VERSION" | tr -d '[:space:]')
70+
71+
echo "Comparing versions:"
72+
echo " Tag version: [$TAG_VERSION]"
73+
echo " Podspec version: [$PODSPEC_VERSION]"
74+
75+
if [ "$PODSPEC_VERSION" != "$TAG_VERSION" ]; then
76+
echo "❌ Version mismatch!"
77+
exit 1
78+
fi
79+
echo "✅ Version check passed: $PODSPEC_VERSION"
80+
81+
- name: Validate Podspec
82+
run: bundle exec pod lib lint Dimina.podspec --allow-warnings --verbose
83+
84+
- name: Publish to CocoaPods
85+
if: env.COCOAPODS_TRUNK_TOKEN != ''
86+
run: bundle exec pod trunk push Dimina.podspec --allow-warnings
87+
env:
88+
COCOAPODS_TRUNK_TOKEN: ${{secrets.COCOAPODS_TRUNK_TOKEN}}
89+
90+
- name: Skip CocoaPods publish
91+
if: env.COCOAPODS_TRUNK_TOKEN == ''
92+
run: |
93+
echo "⚠️ COCOAPODS_TRUNK_TOKEN not configured"
94+
echo "Skipping CocoaPods publish. See .github/COCOAPODS_SETUP.md for setup instructions."

Dimina.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'Dimina'
3-
s.version = '1.1.3'
3+
s.version = '1.2.0'
44
s.summary = 'DiDi Mini Program Framework'
55

66
s.description = <<-DESC

Gemfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
source 'https://rubygems.org'
2+
3+
gem 'cocoapods', '~> 1.15'

android/dimina/build.gradle.kts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ android {
2323
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
2424
consumerProguardFiles("consumer-rules.pro")
2525

26+
buildConfigField("String", "SDK_VERSION", "\"${project.property("DIMINA_VERSION")}\"")
27+
2628
// Only include ARM architectures
2729
ndk {
2830
abiFilters.add("arm64-v8a")
@@ -44,6 +46,7 @@ android {
4446
}
4547
buildFeatures {
4648
compose = true
49+
buildConfig = true
4750
}
4851

4952
// Add configuration to not compress jsapp files
@@ -62,6 +65,7 @@ dependencies {
6265
implementation(libs.androidx.ui)
6366
implementation(libs.androidx.ui.graphics)
6467
implementation(libs.androidx.material3)
68+
implementation(libs.androidx.webkit)
6569
implementation(libs.androidx.compose.material.icons.core)
6670
implementation(libs.kotlinx.serialization.json)
6771
implementation(libs.mmkv)
@@ -117,4 +121,4 @@ tasks.register<Copy>("copySharedJssdkToAssets") {
117121
// Make the preBuild task depend on the copy tasks
118122
tasks.named("preBuild") {
119123
dependsOn("copySharedJssdkToAssets")
120-
}
124+
}

android/dimina/src/main/kotlin/com/didi/dimina/Dimina.kt

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.didi.dimina
22

33
import android.app.Activity
44
import android.content.Context
5+
import com.didi.dimina.api.ext.ExtModuleHandler
56
import com.didi.dimina.bean.MiniProgram
67
import com.didi.dimina.common.LogUtils
78
import com.didi.dimina.common.StoreUtils
@@ -43,15 +44,22 @@ class Dimina private constructor(context: Context) {
4344
// 配置类
4445
class DiminaConfig private constructor(builder: Builder) {
4546
val debugMode: Boolean = builder.debugMode
47+
val apiNamespaces: List<String> = builder.apiNamespaces
4648

4749
class Builder {
4850
var debugMode: Boolean = false
51+
internal var apiNamespaces: MutableList<String> = mutableListOf()
4952

5053
fun setDebugMode(debugMode: Boolean): Builder {
5154
this.debugMode = debugMode
5255
return this
5356
}
5457

58+
fun addApiNamespace(name: String): Builder {
59+
apiNamespaces.add(name)
60+
return this
61+
}
62+
5563
fun build(): DiminaConfig {
5664
return DiminaConfig(this)
5765
}
@@ -66,6 +74,8 @@ class Dimina private constructor(context: Context) {
6674
return config.debugMode
6775
}
6876

77+
fun getApiNamespaces(): List<String> = config.apiNamespaces
78+
6979
private val appContext: Context = context
7080
private lateinit var config: DiminaConfig
7181

@@ -105,4 +115,36 @@ class Dimina private constructor(context: Context) {
105115
val miniApp = MiniApp.getInstance()
106116
miniApp.openApp(context, miniProgram)
107117
}
118+
119+
/**
120+
* 注册第三方扩展 bridge 模块。
121+
*
122+
* 小程序通过 `wx.extBridge` / `wx.extOnBridge` / `wx.extOffBridge` 与 native 模块通信,
123+
* 宿主通过此方法向框架注册对应的处理器。建议在 [init] 之后、[startMiniProgram] 之前调用。
124+
*
125+
* 示例:
126+
* ```kotlin
127+
* Dimina.getInstance().registerExtModule("MyModule") { event, data, callback ->
128+
* when (event) {
129+
* "doSomething" -> {
130+
* // 一次性调用
131+
* callback.onSuccess(JSONObject().apply { put("result", 42) })
132+
* null
133+
* }
134+
* "onDataChange" -> {
135+
* // 持续订阅,返回取消函数
136+
* val listener = registerListener { res -> callback.onSuccess(res) }
137+
* Runnable { unregisterListener(listener) }
138+
* }
139+
* else -> { callback.onFail(JSONObject().apply { put("errMsg", "unknown event") }); null }
140+
* }
141+
* }
142+
* ```
143+
*
144+
* @param moduleName 模块名,与小程序侧 `module` 参数一致
145+
* @param handler 处理器,参见 [ExtModuleHandler]
146+
*/
147+
fun registerExtModule(moduleName: String, handler: ExtModuleHandler) {
148+
MiniApp.getInstance().registerExtModule(moduleName, handler)
149+
}
108150
}

android/dimina/src/main/kotlin/com/didi/dimina/api/ApiRegistry.kt

Lines changed: 98 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.didi.dimina.api
22

3+
import com.didi.dimina.api.ext.ExtBridgeApi
4+
import com.didi.dimina.api.ext.ExtModuleHandler
5+
import com.didi.dimina.common.ApiUtils
36
import com.didi.dimina.common.LogUtils
47
import com.didi.dimina.ui.container.DiminaActivity
58
import org.json.JSONObject
@@ -12,33 +15,120 @@ class ApiRegistry {
1215
private val tag = "ApiRegistry"
1316
private val apiHandlers = mutableMapOf<String, ApiHandler>()
1417

18+
// 宿主注册的第三方扩展模块,key=moduleName
19+
private val extModules = mutableMapOf<String, ExtModuleHandler>()
20+
21+
// extBridge 一次性调用的处理器,共享 extModules 引用
22+
private val extBridgeApi by lazy { ExtBridgeApi(extModules) }
23+
24+
// extOnBridge 持续订阅的取消函数,key="${module}_${event}"
25+
private val extSubscriptions = mutableMapOf<String, Runnable>()
26+
1527
/**
1628
* Registers an API handler
1729
*/
1830
fun register(name: String, handler: ApiHandler) {
1931
apiHandlers[name] = handler
2032
}
21-
33+
34+
/**
35+
* 注册第三方扩展 bridge 模块
36+
* @param moduleName 模块名,对应 extBridge/extOnBridge 的 module 参数
37+
* @param handler 模块处理器
38+
*/
39+
fun registerExtModule(moduleName: String, handler: ExtModuleHandler) {
40+
extModules[moduleName] = handler
41+
LogUtils.d(tag, "Registered ext module: $moduleName")
42+
}
43+
2244
/**
2345
* Invokes an API
24-
*
46+
*
2547
* @param apiName The name of the API to invoke
2648
* @param params Parameters for the API call
2749
* @return True if API was successfully invoked, false otherwise
2850
*/
2951
fun invoke(
3052
activity: DiminaActivity,
3153
appId: String,
32-
apiName: String,
54+
apiName: String,
55+
params: JSONObject,
56+
responseCallback: (String) -> Unit,
57+
): APIResult {
58+
// 1. 优先命中已注册的标准 API
59+
apiHandlers[apiName]?.let { handler ->
60+
return handler.handleAction(activity, appId, apiName, params, responseCallback)
61+
}
62+
63+
// 2. extBridge:未命中且 params 携带 "module" 字段
64+
if (params.has("module")) {
65+
return extBridgeApi.handleAction(activity, appId, apiName, params, responseCallback)
66+
}
67+
68+
// 3. extOnBridge / extOffBridge:apiName 格式为 "${module}_${event}"
69+
val matchedModule = extModules.keys.firstOrNull { apiName.startsWith("${it}_") }
70+
if (matchedModule != null) {
71+
val event = apiName.removePrefix("${matchedModule}_")
72+
val successId = params.optString("success", "")
73+
return if (successId.isNotEmpty()) {
74+
handleExtOnBridge(matchedModule, event, apiName, params, successId, responseCallback)
75+
} else {
76+
handleExtOffBridge(apiName)
77+
NoneResult()
78+
}
79+
}
80+
81+
LogUtils.e(tag, "API not found: $apiName")
82+
return NoneResult()
83+
}
84+
85+
/**
86+
* 处理 extOnBridge:启动持续订阅,保存取消函数
87+
*/
88+
private fun handleExtOnBridge(
89+
module: String,
90+
event: String,
91+
eventKey: String,
3392
params: JSONObject,
93+
successCallbackId: String,
3494
responseCallback: (String) -> Unit,
3595
): APIResult {
36-
val handler = apiHandlers[apiName]
37-
if (handler == null) {
38-
LogUtils.e(tag, "API not found: $apiName")
39-
return NoneResult()
96+
val handler = extModules[module] ?: return NoneResult()
97+
98+
// 若已有相同订阅,先取消旧的
99+
extSubscriptions.remove(eventKey)?.run()
100+
101+
val callback = object : com.didi.dimina.api.ext.ExtCallback {
102+
override fun onSuccess(result: JSONObject) {
103+
responseCallback(ApiUtils.createCallbackResponse(successCallbackId, result))
104+
}
105+
106+
override fun onFail(error: JSONObject) {
107+
LogUtils.e(tag, "extOnBridge error ($eventKey): $error")
108+
}
109+
}
110+
111+
val unsubscribe = handler.handle(event, JSONObject(), callback)
112+
if (unsubscribe != null) {
113+
extSubscriptions[eventKey] = unsubscribe
40114
}
41-
return handler.handleAction(activity, appId, apiName, params, responseCallback)
115+
return NoneResult()
116+
}
117+
118+
/**
119+
* 处理 extOffBridge:取消持续订阅
120+
*/
121+
private fun handleExtOffBridge(eventKey: String) {
122+
extSubscriptions.remove(eventKey)?.run()
123+
LogUtils.d(tag, "extOffBridge: cancelled subscription for $eventKey")
124+
}
125+
126+
/**
127+
* 取消所有持续订阅(小程序销毁时调用)
128+
*/
129+
fun clearExtSubscriptions() {
130+
extSubscriptions.values.forEach { it.run() }
131+
extSubscriptions.clear()
42132
}
43133

44134
/**

android/dimina/src/main/kotlin/com/didi/dimina/api/base/SystemApi.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import android.provider.Settings
1616
import android.util.DisplayMetrics
1717
import androidx.core.app.ActivityCompat
1818
import androidx.core.content.ContextCompat
19+
import com.didi.dimina.BuildConfig
1920
import com.didi.dimina.api.APIResult
2021
import com.didi.dimina.api.AsyncResult
2122
import com.didi.dimina.api.BaseApiHandler
@@ -179,6 +180,7 @@ class SystemApi : BaseApiHandler() {
179180
put("version", Build.VERSION.RELEASE)
180181
put("system", "Android ${Build.VERSION.RELEASE}") // 操作系统及版本
181182
put("platform", "android")
183+
put("SDKVersion", BuildConfig.SDK_VERSION)
182184
put("deviceOrientation", currentActivity.resources.configuration.orientation.let {
183185
when (it) {
184186
Configuration.ORIENTATION_PORTRAIT -> "portrait"

0 commit comments

Comments
 (0)