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消息显示方式为扩展函数实现
这个提交包含在:
父节点
a3e87727b7
当前提交
e389f9fda8
@ -1,6 +1,5 @@
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
|
||||
@ -56,9 +55,6 @@ android {
|
||||
jniLibs.srcDirs = ['libs']
|
||||
}
|
||||
}
|
||||
androidExtensions {
|
||||
experimental = true
|
||||
}
|
||||
namespace 'com.nova.brain.glass'
|
||||
|
||||
packagingOptions {
|
||||
@ -80,9 +76,10 @@ dependencies {
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
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"
|
||||
}
|
||||
implementation 'com.rokid.security.sdk:online-speech:0.1.0'
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.nova.brain.glass;
|
||||
|
||||
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.repository.HeaderInterceptor;
|
||||
import com.rokid.security.glass3.open.sdk.GlassSdk;
|
||||
@ -42,6 +43,7 @@ public class MyApplication extends App {
|
||||
@Override
|
||||
public void onServiceConnected() {
|
||||
OfflineCmdServiceHelper.INSTANCE.init();
|
||||
AsrHelper.INSTANCE.init();
|
||||
}
|
||||
|
||||
@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.rokid.utils.ContextUtil.getApplicationContext
|
||||
import com.xuqm.base.di.manager.HttpManager
|
||||
import com.xuqm.base.extensions.showMessage
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
@ -44,7 +45,6 @@ object IntentRecognizeHelper {
|
||||
* @param onComplete 无论成功失败都会回调,用于调用方重置 loading 状态
|
||||
*/
|
||||
fun recognize(
|
||||
context: Context,
|
||||
text: String? = null,
|
||||
scence: String = "home",
|
||||
onSuccess: (action: RecognizeAction) -> Unit,
|
||||
@ -64,11 +64,11 @@ object IntentRecognizeHelper {
|
||||
if (model.code == "0") {
|
||||
onSuccess(model.data.action)
|
||||
} else {
|
||||
Toast.makeText(context, model.message, Toast.LENGTH_SHORT).show()
|
||||
model.message.showMessage()
|
||||
}
|
||||
onComplete() // 无论成功失败都执行
|
||||
}, { e ->
|
||||
Toast.makeText(context, "请求失败: ${e.message}", Toast.LENGTH_SHORT).show()
|
||||
"请求失败: ${e.message}".showMessage()
|
||||
onComplete()
|
||||
})
|
||||
}
|
||||
|
||||
@ -120,6 +120,11 @@ object OfflineCmdServiceHelper {
|
||||
// 通用关键词在 init 时注册一次,页面切换不会移除它们
|
||||
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){
|
||||
this.listenerList.add(listener)
|
||||
}
|
||||
|
||||
@ -8,8 +8,6 @@ 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
|
||||
@ -38,9 +36,9 @@ class HeaderInterceptor(val context: Context) : Interceptor {
|
||||
// context.getStringForPreferences(SHARE_UESR_TOKEN).loge()
|
||||
|
||||
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)
|
||||
|
||||
@ -50,7 +48,7 @@ class HeaderInterceptor(val context: Context) : Interceptor {
|
||||
return response
|
||||
}
|
||||
|
||||
response.body()?.also {
|
||||
response.body?.also {
|
||||
if (!bodyHasUnknownEncoding(headers) && hasBody(response)) {
|
||||
val source: BufferedSource = it.source()
|
||||
source.request(Long.MAX_VALUE) // Buffer the entire body.
|
||||
@ -108,11 +106,11 @@ class HeaderInterceptor(val context: Context) : Interceptor {
|
||||
|
||||
fun hasBody(response: Response): Boolean {
|
||||
// HEAD requests never yield a body regardless of the response headers.
|
||||
if (response.request().method() == "HEAD") {
|
||||
if (response.request.method == "HEAD") {
|
||||
return false
|
||||
}
|
||||
val responseCode = response.code()
|
||||
if ((responseCode < StatusLine.HTTP_CONTINUE || responseCode >= 200)
|
||||
val responseCode = response.code
|
||||
if ((responseCode < 100 || responseCode >= 200)
|
||||
&& responseCode != HttpURLConnection.HTTP_NO_CONTENT && responseCode != HttpURLConnection.HTTP_NOT_MODIFIED
|
||||
) {
|
||||
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
|
||||
// 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"),
|
||||
ignoreCase = true
|
||||
)
|
||||
|
||||
@ -4,8 +4,8 @@ import android.view.View
|
||||
import android.widget.TextView
|
||||
import com.nova.brain.glass.R
|
||||
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.IntentRecognizeHelper
|
||||
import com.nova.brain.glass.helper.OfflineCmdListener
|
||||
import com.nova.brain.glass.helper.OfflineCmdServiceHelper
|
||||
import com.nova.brain.glass.model.ChatItem
|
||||
@ -54,31 +54,8 @@ class ChatActivity : BaseListFormLayoutNormalActivity<ChatItem, ChatVM, Activity
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 调用意图识别,识别成功后发起 SSE 请求。
|
||||
* 立即启动 pb 旋转,不等 recognize 接口响应。
|
||||
*/
|
||||
private fun recognizeAndChat() {
|
||||
binding.pb.visibility = View.VISIBLE
|
||||
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
|
||||
}
|
||||
)
|
||||
// 识别由 AsrHelper 在 onFinalResult 中统一触发,此处无需主动发起
|
||||
}
|
||||
|
||||
private fun scrollToBottom() {
|
||||
@ -116,15 +93,19 @@ class ChatActivity : BaseListFormLayoutNormalActivity<ChatItem, ChatVM, Activity
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
OfflineCmdServiceHelper.addOnLineListener(listener)
|
||||
AsrHelper.scene = "decision"
|
||||
AsrHelper.onGoToDecisionCenter = { action ->
|
||||
viewModel.demoPostSse(action.params.question)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
OfflineCmdServiceHelper.removeOnLineListener(listener)
|
||||
AsrHelper.onGoToDecisionCenter = null
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
IntentRecognizeHelper.dispose()
|
||||
}
|
||||
}
|
||||
@ -7,7 +7,7 @@ import android.os.Looper
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.nova.brain.glass.R
|
||||
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.OfflineCmdServiceHelper
|
||||
import com.nova.brain.glass.viewmodel.WelcomeVM
|
||||
@ -47,34 +47,12 @@ class WelcomeActivity : BaseActivity<ActivityWelcomeBinding>() {
|
||||
super.initView(savedInstanceState)
|
||||
vm = ViewModelProvider(this)[WelcomeVM::class.java]
|
||||
window.addFlags(android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
binding.tv.setOnClickListener {
|
||||
triggerRecognize()
|
||||
}
|
||||
}
|
||||
|
||||
override fun 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 {
|
||||
override fun onOfflineCmd(cmd: String) {
|
||||
runOnUiThread {
|
||||
@ -82,9 +60,6 @@ class WelcomeActivity : BaseActivity<ActivityWelcomeBinding>() {
|
||||
"任务列表", "查看任务", "查看任务列表" -> {
|
||||
startActivity(Intent(this@WelcomeActivity, TaskListActivity::class.java))
|
||||
}
|
||||
"决策中心", "紧急任务", "当前任务" -> {
|
||||
triggerRecognize()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,6 +70,13 @@ class WelcomeActivity : BaseActivity<ActivityWelcomeBinding>() {
|
||||
OfflineCmdServiceHelper.addOnLineListener(offlineCmdListener)
|
||||
OfflineCmdServiceHelper.addListenerWelcome()
|
||||
stopDotsAnim() // 从 ChatActivity 返回时恢复原文
|
||||
AsrHelper.scene = "home"
|
||||
AsrHelper.onGoToDecisionCenter = { action ->
|
||||
startActivity(
|
||||
Intent(this, ChatActivity::class.java)
|
||||
.putExtra("question", action.params.question)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@ -102,6 +84,7 @@ class WelcomeActivity : BaseActivity<ActivityWelcomeBinding>() {
|
||||
OfflineCmdServiceHelper.removeOnLineListener(offlineCmdListener)
|
||||
OfflineCmdServiceHelper.removeListenerWelcome()
|
||||
dotsHandler.removeCallbacks(dotsRunnable)
|
||||
AsrHelper.onGoToDecisionCenter = null
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
|
||||
@ -6,7 +6,7 @@ import com.xuqm.base.App
|
||||
import com.xuqm.base.di.manager.HttpManager
|
||||
import com.xuqm.sdhbwfu.core.viewModel.BaseViewModel
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.RequestBody
|
||||
|
||||
class WelcomeVM : BaseViewModel() {
|
||||
@ -26,7 +26,7 @@ class WelcomeVM : BaseViewModel() {
|
||||
fun demoPost() {
|
||||
result.value = "POST 请求中..."
|
||||
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)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe({ resp ->
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
apply from: "config.gradle"
|
||||
buildscript {
|
||||
ext.kotlin_version = "1.6.10"
|
||||
ext.kotlin_version = "2.2.0"
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://nexus.xuqinmin.com/repository/android/' }
|
||||
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/repository/google/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/public/' }
|
||||
@ -18,8 +19,8 @@ buildscript {
|
||||
// }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.2.0'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.7.0"
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.0"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
@ -33,6 +34,7 @@ allprojects {
|
||||
// maven { url 'http://developer.huawei.com/repo/' }
|
||||
maven { url 'https://nexus.xuqinmin.com/repository/android/' }
|
||||
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/repository/google/' }
|
||||
maven { url 'https://maven.aliyun.com/repository/public/' }
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion versions.compileSdk
|
||||
|
||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
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
|
||||
|
||||
正在加载...
在新工单中引用
屏蔽一个用户