feat(asr): 集成ASR助手并升级项目依赖

- 将Gradle版本从7.3.3升级到8.6
- 升级Kotlin版本从1.6.10到2.2.0并更新相关插件
- 升级Android Gradle Plugin到7.4.2
- 集成AsrHelper替代原有的IntentRecognizeHelper进行语音识别
- 添加Nova Nova唤醒词注册功能
- 更新SDK依赖版本并添加新的Maven仓库地址
- 移除废弃的kotlin-android-extensions插件
- 优化HeaderInterceptor中的HTTP响应处理逻辑
- 统一Toast消息显示方式为扩展函数实现
这个提交包含在:
徐勤民 2026-04-16 22:25:23 +08:00
父节点 a3e87727b7
当前提交 e389f9fda8
共有 12 个文件被更改,包括 222 次插入76 次删除

查看文件

@ -1,6 +1,5 @@
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
@ -56,9 +55,6 @@ android {
jniLibs.srcDirs = ['libs'] jniLibs.srcDirs = ['libs']
} }
} }
androidExtensions {
experimental = true
}
namespace 'com.nova.brain.glass' namespace 'com.nova.brain.glass'
packagingOptions { packagingOptions {
@ -80,9 +76,10 @@ dependencies {
implementation 'com.google.android.material:material:1.3.0' implementation 'com.google.android.material:material:1.3.0'
implementation "io.noties.markwon:core:4.6.2" implementation "io.noties.markwon:core:4.6.2"
implementation ('com.rokid.security:glass3.open.sdk:2.1.5-E') { implementation ('com.rokid.security:glass3.open.sdk:2.1.6-E') {
exclude group: "org.slf4j" exclude group: "org.slf4j"
} }
implementation 'com.rokid.security.sdk:online-speech:0.1.0'
} }

查看文件

@ -1,6 +1,7 @@
package com.nova.brain.glass; package com.nova.brain.glass;
import com.blankj.utilcode.util.Utils; import com.blankj.utilcode.util.Utils;
import com.nova.brain.glass.helper.AsrHelper;
import com.nova.brain.glass.helper.OfflineCmdServiceHelper; import com.nova.brain.glass.helper.OfflineCmdServiceHelper;
import com.nova.brain.glass.repository.HeaderInterceptor; import com.nova.brain.glass.repository.HeaderInterceptor;
import com.rokid.security.glass3.open.sdk.GlassSdk; import com.rokid.security.glass3.open.sdk.GlassSdk;
@ -42,6 +43,7 @@ public class MyApplication extends App {
@Override @Override
public void onServiceConnected() { public void onServiceConnected() {
OfflineCmdServiceHelper.INSTANCE.init(); OfflineCmdServiceHelper.INSTANCE.init();
AsrHelper.INSTANCE.init();
} }
@Override @Override

查看文件

@ -0,0 +1,178 @@
package com.nova.brain.glass.helper
import android.util.Log
import com.nova.brain.glass.model.RecognizeAction
import com.rokid.online.speech.AsrClient
import com.rokid.online.speech.OnlineSpeechSdk
import com.rokid.online.speech.OnlineSpeechSdkConfig
import com.rokid.online.speech.open.OpenSdkAudioSource
import com.xuqm.base.extensions.showMessage
object AsrHelper : OfflineCmdListener {
private const val TAG = "AsrHelper"
// 配置信息,从 online-speech-sdk-demo 参考项目复制
private const val DOMAIN = "api-test.rokid.com"
private const val ASR_PATH = "/ar/audio/api/ws/asr/streaming"
private const val TTS_PATH = "/ar/audio/api/ws/tts"
private const val AK = ""
private const val SK = ""
private const val UID = "demo-user"
private const val DEVICE_ID = "demo-device"
// 唤醒词Nova Nova
private const val WAKE_WORD = "Nova Nova"
private const val WAKE_WORD_PINYIN = "nou wa nou wa"
private var sdk: OnlineSpeechSdk? = null
private var asr: AsrClient? = null
private val audioSource = OpenSdkAudioSource()
private var isConnected = false
private var isMicRunning = false
// 拼接每次识别会话中的中间结果
private var currentPartial = ""
// 拼接跨多次识别的最终结果
private val sessionBuilder = StringBuilder()
/** 当前页面的场景标识,由各 Activity 在 onResume/onPause 中维护 */
var scene: String = "home"
/** goToDecisionCenter 命中时的回调,由各 Activity 在 onResume/onPause 中注册/清空 */
var onGoToDecisionCenter: ((action: RecognizeAction) -> Unit)? = null
fun init() {
val cfg = OnlineSpeechSdkConfig(
domain = DOMAIN,
ak = AK,
sk = SK,
uid = UID,
deviceId = DEVICE_ID,
asrPath = ASR_PATH,
ttsPath = TTS_PATH,
trustAllCerts = true,
staticHttpHeaders = mapOf(
"appCredential" to "userInfo",
"messageId" to "android-asr-${System.currentTimeMillis()}",
),
staticMessageHeaders = mapOf(
"appCredential" to "userInfo",
"messageId" to "android-asr-${System.currentTimeMillis()}",
),
)
sdk = OnlineSpeechSdk(cfg)
asr = sdk!!.createAsrClient().attachAudioSource(audioSource).also { setupAsrCallbacks(it) }
// 注册离线关键词 Nova Nova,GlassSdk 触发后启动 ASR
OfflineCmdServiceHelper.registerAsrWakeWord()
OfflineCmdServiceHelper.addOnLineListener(this)
// 自动建立 ASR 连接
asrConnect()
Log.d(TAG, "AsrHelper init done")
}
private fun asrConnect() {
asr?.connect()
Log.d(TAG, "ASR connect() called")
}
private fun asrStartMic() {
if (!isConnected) {
Log.w(TAG, "ASR startMic ignored: not connected")
return
}
if (isMicRunning) {
Log.w(TAG, "ASR startMic ignored: mic already running")
return
}
runCatching { asr?.startAsrWithMic() }
.onSuccess {
isMicRunning = true
Log.d(TAG, "ASR startAsrWithMic()")
}
.onFailure { Log.e(TAG, "ASR startAsrWithMic failed: ${it.message}") }
}
private fun setupAsrCallbacks(asrClient: AsrClient) {
asrClient.setListener(object : AsrClient.Listener {
override fun onOpen() {
isConnected = true
Log.d(TAG, "ASR websocket open")
}
override fun onStart(taskId: String) {
currentPartial = ""
sessionBuilder.clear()
Log.d(TAG, "ASR started: $taskId")
}
override fun onPartialResult(taskId: String, text: String) {
// 滚动更新当前识别中间结果
currentPartial = text
Log.d(TAG, "ASR partial: $text")
}
override fun onFinalResult(taskId: String, text: String) {
// 将最终结果追加拼接到会话字符串
sessionBuilder.append(text)
val fullText = sessionBuilder.toString()
isMicRunning = false
Log.d(TAG, "ASR final result: $fullText")
IntentRecognizeHelper.recognize(
text = fullText,
scence = scene,
onSuccess = { action ->
if (action.name == "goToDecisionCenter") {
onGoToDecisionCenter?.invoke(action)
} else {
"需要跳转任务列表".showMessage()
}
}
)
}
override fun onFinished(taskId: String) {
Log.d(TAG, "ASR ended: $taskId")
}
override fun onError(code: Int, message: String) {
Log.e(TAG, "ASR error code=$code msg=$message")
isMicRunning = false
}
override fun onClosed(code: Int, reason: String) {
isConnected = false
isMicRunning = false
Log.d(TAG, "ASR closed code=$code reason=$reason")
}
})
}
// 离线关键词回调:匹配唤醒词时启动麦克风
override fun onOfflineCmd(cmd: String) {
if (cmd == WAKE_WORD) {
Log.d(TAG, "Wake word triggered, starting mic")
asrStartMic()
}
}
fun close() {
OfflineCmdServiceHelper.removeOnLineListener(this)
if (isMicRunning) {
runCatching { asr?.stopAsrWithMic() }
isMicRunning = false
}
asr?.close()
sdk?.close()
asr = null
sdk = null
isConnected = false
sessionBuilder.clear()
Log.d(TAG, "AsrHelper closed")
}
}

查看文件

@ -10,6 +10,7 @@ import com.nova.brain.glass.repository.HeaderInterceptor
import com.nova.brain.glass.repository.Service import com.nova.brain.glass.repository.Service
import com.rokid.utils.ContextUtil.getApplicationContext import com.rokid.utils.ContextUtil.getApplicationContext
import com.xuqm.base.di.manager.HttpManager import com.xuqm.base.di.manager.HttpManager
import com.xuqm.base.extensions.showMessage
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
@ -44,7 +45,6 @@ object IntentRecognizeHelper {
* @param onComplete 无论成功失败都会回调用于调用方重置 loading 状态 * @param onComplete 无论成功失败都会回调用于调用方重置 loading 状态
*/ */
fun recognize( fun recognize(
context: Context,
text: String? = null, text: String? = null,
scence: String = "home", scence: String = "home",
onSuccess: (action: RecognizeAction) -> Unit, onSuccess: (action: RecognizeAction) -> Unit,
@ -64,11 +64,11 @@ object IntentRecognizeHelper {
if (model.code == "0") { if (model.code == "0") {
onSuccess(model.data.action) onSuccess(model.data.action)
} else { } else {
Toast.makeText(context, model.message, Toast.LENGTH_SHORT).show() model.message.showMessage()
} }
onComplete() // 无论成功失败都执行 onComplete() // 无论成功失败都执行
}, { e -> }, { e ->
Toast.makeText(context, "请求失败: ${e.message}", Toast.LENGTH_SHORT).show() "请求失败: ${e.message}".showMessage()
onComplete() onComplete()
}) })
} }

查看文件

@ -120,6 +120,11 @@ object OfflineCmdServiceHelper {
// 通用关键词在 init 时注册一次,页面切换不会移除它们 // 通用关键词在 init 时注册一次,页面切换不会移除它们
addCommonCmds() addCommonCmds()
} }
// 注册 ASR 唤醒词(由 AsrHelper 调用)
fun registerAsrWakeWord() {
registerBeans(listOf(OfflineCmdBean("Nova Nova", "nou wa nou wa"),OfflineCmdBean("Nova Nova", "nao wa nao wa")))
}
fun addOnLineListener(listener: OfflineCmdListener){ fun addOnLineListener(listener: OfflineCmdListener){
this.listenerList.add(listener) this.listenerList.add(listener)
} }

查看文件

@ -8,8 +8,6 @@ import com.xuqm.base.extensions.loge
import okhttp3.Headers import okhttp3.Headers
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.Response import okhttp3.Response
import okhttp3.internal.http.HttpHeaders
import okhttp3.internal.http.StatusLine
import okio.Buffer import okio.Buffer
import okio.BufferedSource import okio.BufferedSource
import okio.GzipSource import okio.GzipSource
@ -38,9 +36,9 @@ class HeaderInterceptor(val context: Context) : Interceptor {
// context.getStringForPreferences(SHARE_UESR_TOKEN).loge() // context.getStringForPreferences(SHARE_UESR_TOKEN).loge()
val request = requestBuilder.build() val request = requestBuilder.build()
"${request.url()}(${request.method()})".loge() "${request.url}(${request.method})".loge()
val headers = request.headers() val headers = request.headers
val response = chain.proceed(request) val response = chain.proceed(request)
@ -50,7 +48,7 @@ class HeaderInterceptor(val context: Context) : Interceptor {
return response return response
} }
response.body()?.also { response.body?.also {
if (!bodyHasUnknownEncoding(headers) && hasBody(response)) { if (!bodyHasUnknownEncoding(headers) && hasBody(response)) {
val source: BufferedSource = it.source() val source: BufferedSource = it.source()
source.request(Long.MAX_VALUE) // Buffer the entire body. source.request(Long.MAX_VALUE) // Buffer the entire body.
@ -108,11 +106,11 @@ class HeaderInterceptor(val context: Context) : Interceptor {
fun hasBody(response: Response): Boolean { fun hasBody(response: Response): Boolean {
// HEAD requests never yield a body regardless of the response headers. // HEAD requests never yield a body regardless of the response headers.
if (response.request().method() == "HEAD") { if (response.request.method == "HEAD") {
return false return false
} }
val responseCode = response.code() val responseCode = response.code
if ((responseCode < StatusLine.HTTP_CONTINUE || responseCode >= 200) if ((responseCode < 100 || responseCode >= 200)
&& responseCode != HttpURLConnection.HTTP_NO_CONTENT && responseCode != HttpURLConnection.HTTP_NOT_MODIFIED && responseCode != HttpURLConnection.HTTP_NO_CONTENT && responseCode != HttpURLConnection.HTTP_NOT_MODIFIED
) { ) {
return true return true
@ -120,7 +118,8 @@ class HeaderInterceptor(val context: Context) : Interceptor {
// If the Content-Length or Transfer-Encoding headers disagree with the response code, the // If the Content-Length or Transfer-Encoding headers disagree with the response code, the
// response is malformed. For best compatibility, we honor the headers. // response is malformed. For best compatibility, we honor the headers.
return HttpHeaders.contentLength(response) != -1L || "chunked".equals( val contentLength = response.headers["Content-Length"]?.toLongOrNull() ?: -1L
return contentLength != -1L || "chunked".equals(
response.header("Transfer-Encoding"), response.header("Transfer-Encoding"),
ignoreCase = true ignoreCase = true
) )

查看文件

@ -4,8 +4,8 @@ import android.view.View
import android.widget.TextView import android.widget.TextView
import com.nova.brain.glass.R import com.nova.brain.glass.R
import com.nova.brain.glass.databinding.ActivityChatBinding import com.nova.brain.glass.databinding.ActivityChatBinding
import com.nova.brain.glass.helper.AsrHelper
import com.nova.brain.glass.helper.BgChatDrawable import com.nova.brain.glass.helper.BgChatDrawable
import com.nova.brain.glass.helper.IntentRecognizeHelper
import com.nova.brain.glass.helper.OfflineCmdListener import com.nova.brain.glass.helper.OfflineCmdListener
import com.nova.brain.glass.helper.OfflineCmdServiceHelper import com.nova.brain.glass.helper.OfflineCmdServiceHelper
import com.nova.brain.glass.model.ChatItem import com.nova.brain.glass.model.ChatItem
@ -54,31 +54,8 @@ class ChatActivity : BaseListFormLayoutNormalActivity<ChatItem, ChatVM, Activity
} }
} }
/**
* 调用意图识别识别成功后发起 SSE 请求
* 立即启动 pb 旋转不等 recognize 接口响应
*/
private fun recognizeAndChat() { private fun recognizeAndChat() {
binding.pb.visibility = View.VISIBLE // 识别由 AsrHelper 在 onFinalResult 中统一触发,此处无需主动发起
binding.pb1.visibility = View.INVISIBLE
IntentRecognizeHelper.recognize(
context = this,
scence = "decision",
onSuccess = { action ->
if (action.name == "goToDecisionCenter") {
viewModel.demoPostSse(action.params.question)
} else {
// 识别成功但没有匹配动作,停止旋转
binding.pb.isIndeterminate = false
binding.pb.visibility = View.INVISIBLE
}
},
onComplete = {
// recognize 失败时兜底停止旋转(成功路径由 loading LiveData 接管)
binding.pb.isIndeterminate = false
binding.pb.visibility = View.INVISIBLE
}
)
} }
private fun scrollToBottom() { private fun scrollToBottom() {
@ -116,15 +93,19 @@ class ChatActivity : BaseListFormLayoutNormalActivity<ChatItem, ChatVM, Activity
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
OfflineCmdServiceHelper.addOnLineListener(listener) OfflineCmdServiceHelper.addOnLineListener(listener)
AsrHelper.scene = "decision"
AsrHelper.onGoToDecisionCenter = { action ->
viewModel.demoPostSse(action.params.question)
}
} }
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
OfflineCmdServiceHelper.removeOnLineListener(listener) OfflineCmdServiceHelper.removeOnLineListener(listener)
AsrHelper.onGoToDecisionCenter = null
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
IntentRecognizeHelper.dispose()
} }
} }

查看文件

@ -7,7 +7,7 @@ import android.os.Looper
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.nova.brain.glass.R import com.nova.brain.glass.R
import com.nova.brain.glass.databinding.ActivityWelcomeBinding import com.nova.brain.glass.databinding.ActivityWelcomeBinding
import com.nova.brain.glass.helper.IntentRecognizeHelper import com.nova.brain.glass.helper.AsrHelper
import com.nova.brain.glass.helper.OfflineCmdListener import com.nova.brain.glass.helper.OfflineCmdListener
import com.nova.brain.glass.helper.OfflineCmdServiceHelper import com.nova.brain.glass.helper.OfflineCmdServiceHelper
import com.nova.brain.glass.viewmodel.WelcomeVM import com.nova.brain.glass.viewmodel.WelcomeVM
@ -47,34 +47,12 @@ class WelcomeActivity : BaseActivity<ActivityWelcomeBinding>() {
super.initView(savedInstanceState) super.initView(savedInstanceState)
vm = ViewModelProvider(this)[WelcomeVM::class.java] vm = ViewModelProvider(this)[WelcomeVM::class.java]
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
binding.tv.setOnClickListener {
triggerRecognize()
}
} }
override fun initData() { override fun initData() {
super.initData() super.initData()
} }
private fun triggerRecognize() {
startDotsAnim()
IntentRecognizeHelper.recognize(
context = this,
text = "当前阶段,最紧急的任务是什么?",
onSuccess = { action ->
if (action.name == "goToDecisionCenter") {
startActivity(
Intent(this, ChatActivity::class.java)
.putExtra("question", action.params.question)
)
}
},
onComplete = {
stopDotsAnim()
}
)
}
private val offlineCmdListener = object : OfflineCmdListener { private val offlineCmdListener = object : OfflineCmdListener {
override fun onOfflineCmd(cmd: String) { override fun onOfflineCmd(cmd: String) {
runOnUiThread { runOnUiThread {
@ -82,9 +60,6 @@ class WelcomeActivity : BaseActivity<ActivityWelcomeBinding>() {
"任务列表", "查看任务", "查看任务列表" -> { "任务列表", "查看任务", "查看任务列表" -> {
startActivity(Intent(this@WelcomeActivity, TaskListActivity::class.java)) startActivity(Intent(this@WelcomeActivity, TaskListActivity::class.java))
} }
"决策中心", "紧急任务", "当前任务" -> {
triggerRecognize()
}
} }
} }
} }
@ -95,6 +70,13 @@ class WelcomeActivity : BaseActivity<ActivityWelcomeBinding>() {
OfflineCmdServiceHelper.addOnLineListener(offlineCmdListener) OfflineCmdServiceHelper.addOnLineListener(offlineCmdListener)
OfflineCmdServiceHelper.addListenerWelcome() OfflineCmdServiceHelper.addListenerWelcome()
stopDotsAnim() // 从 ChatActivity 返回时恢复原文 stopDotsAnim() // 从 ChatActivity 返回时恢复原文
AsrHelper.scene = "home"
AsrHelper.onGoToDecisionCenter = { action ->
startActivity(
Intent(this, ChatActivity::class.java)
.putExtra("question", action.params.question)
)
}
} }
override fun onPause() { override fun onPause() {
@ -102,6 +84,7 @@ class WelcomeActivity : BaseActivity<ActivityWelcomeBinding>() {
OfflineCmdServiceHelper.removeOnLineListener(offlineCmdListener) OfflineCmdServiceHelper.removeOnLineListener(offlineCmdListener)
OfflineCmdServiceHelper.removeListenerWelcome() OfflineCmdServiceHelper.removeListenerWelcome()
dotsHandler.removeCallbacks(dotsRunnable) dotsHandler.removeCallbacks(dotsRunnable)
AsrHelper.onGoToDecisionCenter = null
} }
override fun onDestroy() { override fun onDestroy() {

查看文件

@ -6,7 +6,7 @@ import com.xuqm.base.App
import com.xuqm.base.di.manager.HttpManager import com.xuqm.base.di.manager.HttpManager
import com.xuqm.sdhbwfu.core.viewModel.BaseViewModel import com.xuqm.sdhbwfu.core.viewModel.BaseViewModel
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody import okhttp3.RequestBody
class WelcomeVM : BaseViewModel() { class WelcomeVM : BaseViewModel() {
@ -26,7 +26,7 @@ class WelcomeVM : BaseViewModel() {
fun demoPost() { fun demoPost() {
result.value = "POST 请求中..." result.value = "POST 请求中..."
val json = """{"demo":"post","from":"glass"}""" val json = """{"demo":"post","from":"glass"}"""
val body = RequestBody.create(MediaType.parse("application/json"), json) val body = RequestBody.create("application/json".toMediaTypeOrNull(), json)
HttpManager.getApi(Service::class.java).demoPost(body) HttpManager.getApi(Service::class.java).demoPost(body)
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe({ resp -> .subscribe({ resp ->

查看文件

@ -1,12 +1,13 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
apply from: "config.gradle" apply from: "config.gradle"
buildscript { buildscript {
ext.kotlin_version = "1.6.10" ext.kotlin_version = "2.2.0"
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven { url 'https://nexus.xuqinmin.com/repository/android/' } maven { url 'https://nexus.xuqinmin.com/repository/android/' }
maven { url 'https://maven.rokid.com/repository/maven-public/' } maven { url 'https://maven.rokid.com/repository/maven-public/' }
maven { url 'https://maven.rokid-inc.com/repository/maven-public/' }
maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/' } maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/' }
maven { url 'https://maven.aliyun.com/repository/google/' } maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/public/' }
@ -18,8 +19,8 @@ buildscript {
// } // }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:7.2.0' classpath 'com.android.tools.build:gradle:7.4.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.0" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.0"
// NOTE: Do not place your application dependencies here; they belong // NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files // in the individual module build.gradle files
@ -33,6 +34,7 @@ allprojects {
// maven { url 'http://developer.huawei.com/repo/' } // maven { url 'http://developer.huawei.com/repo/' }
maven { url 'https://nexus.xuqinmin.com/repository/android/' } maven { url 'https://nexus.xuqinmin.com/repository/android/' }
maven { url 'https://maven.rokid.com/repository/maven-public/' } maven { url 'https://maven.rokid.com/repository/maven-public/' }
maven { url 'https://maven.rokid-inc.com/repository/maven-public/' }
maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/' } maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/' }
maven { url 'https://maven.aliyun.com/repository/google/' } maven { url 'https://maven.aliyun.com/repository/google/' }
maven { url 'https://maven.aliyun.com/repository/public/' } maven { url 'https://maven.aliyun.com/repository/public/' }

查看文件

@ -1,6 +1,5 @@
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android { android {
compileSdkVersion versions.compileSdk compileSdkVersion versions.compileSdk

查看文件

@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip