Compare commits
10 Commits
ec17b6d633
...
d87dd7d6a5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d87dd7d6a5 | ||
|
|
8d5f093355 | ||
|
|
724fbbadb0 | ||
|
|
2c4cf902af | ||
|
|
63ac5bb7e2 | ||
|
|
e4bd908714 | ||
|
|
76caabd295 | ||
|
|
9af378cec7 | ||
|
|
964aaa9edc | ||
|
|
ec36a966f5 |
@@ -5,7 +5,7 @@ ext {
|
||||
|
||||
compileSdkVersion: 34,
|
||||
buildToolsVersion: "29.0.2",
|
||||
minSdkVersion : 19,
|
||||
minSdkVersion : 21,
|
||||
targetSdkVersion : 29,
|
||||
]
|
||||
dependencies = [
|
||||
|
||||
@@ -11,17 +11,19 @@ import okhttp3.Response
|
||||
class HttpHeaderInterceptor : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val original = chain.request()
|
||||
val aiCloudClientConfig = MoGoAiCloudClient.getInstance().aiCloudClientConfig
|
||||
val iHttpCurrentLocation = aiCloudClientConfig.iHttpCurrentLocation.currentLocation
|
||||
var request = original.newBuilder()
|
||||
.header("token", MoGoAiCloudClient.getInstance().aiCloudClientConfig.token)
|
||||
.header("cityCode", MoGoAiCloudClient.getInstance().aiCloudClientConfig.iHttpCurrentLocation.currentLocation?.cityCode.toString())
|
||||
.header("lat",MoGoAiCloudClient.getInstance().aiCloudClientConfig.iHttpCurrentLocation.currentLocation?.lat.toString())
|
||||
.header("lon",MoGoAiCloudClient.getInstance().aiCloudClientConfig.iHttpCurrentLocation.currentLocation?.lon.toString())
|
||||
.header("env",MoGoAiCloudClient.getInstance().aiCloudClientConfig.netMode.toString())
|
||||
.header("sn",MoGoAiCloudClient.getInstance().aiCloudClientConfig.sn)
|
||||
.header("token", aiCloudClientConfig.token)
|
||||
.header("cityCode", iHttpCurrentLocation?.cityCode.toString())
|
||||
.header("lat", iHttpCurrentLocation?.lat.toString())
|
||||
.header("lon", iHttpCurrentLocation?.lon.toString())
|
||||
.header("env", aiCloudClientConfig.netMode.toString())
|
||||
.header("sn", aiCloudClientConfig.sn)
|
||||
.method(original.method(), original.body())
|
||||
.build()
|
||||
if(MoGoAiCloudClient.getInstance().aiCloudClientConfig.securityKey.isNotBlank()){
|
||||
request = request.newBuilder().header("authKey",MoGoAiCloudClient.getInstance().aiCloudClientConfig.securityKey).build()
|
||||
if(aiCloudClientConfig.securityKey.isNotBlank()){
|
||||
request = request.newBuilder().header("authKey", aiCloudClientConfig.securityKey).build()
|
||||
}
|
||||
return chain.proceed(request)
|
||||
}
|
||||
|
||||
@@ -16,11 +16,14 @@ import static com.mogo.cloud.passport.MoGoAiCloudClient.TAG;
|
||||
import static com.mogo.cloud.passport.MoGoAiCloudClientConfig.HTTP_DNS_ENV_DEV;
|
||||
import static com.mogo.cloud.passport.MoGoAiCloudClientConfig.HTTP_DNS_ENV_QA;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
public class ThirdPassportManager {
|
||||
|
||||
private static volatile ThirdPassportManager mInstance;
|
||||
private IPassport mPassport;
|
||||
|
||||
private AtomicBoolean isRequestIng = new AtomicBoolean(false);
|
||||
|
||||
private ThirdPassportManager() {
|
||||
|
||||
}
|
||||
@@ -37,9 +40,13 @@ public class ThirdPassportManager {
|
||||
}
|
||||
|
||||
public void refreshToken(Context context, IPassport passport) {
|
||||
if(isRequestIng.get()){
|
||||
return;
|
||||
}
|
||||
this.mPassport = passport;
|
||||
MoGoAiCloudClientConfig mAiCloudClientConfig = MoGoAiCloudClient.getInstance().getAiCloudClientConfig();
|
||||
if (mAiCloudClientConfig != null) {
|
||||
isRequestIng.set(true);
|
||||
ThirdLoginParam thirdLoginParam = ThirdLoginParam.of(
|
||||
mAiCloudClientConfig.getThirdPartyDeviceId(),
|
||||
mAiCloudClientConfig.getThirdPartyAppKey()
|
||||
@@ -48,6 +55,7 @@ public class ThirdPassportManager {
|
||||
LoginCallback loginCallback = new LoginCallback() {
|
||||
@Override
|
||||
public void onSuccess(TokenData.TokenResult result) {
|
||||
isRequestIng.set(false);
|
||||
if (mPassport != null) {
|
||||
mPassport.onSuccess(result.token, result.sn);
|
||||
}
|
||||
|
||||
@@ -38,23 +38,23 @@ RELEASE=true
|
||||
# 工具类
|
||||
MOGO_UTILS_VERSION=1.4.7.62
|
||||
# 网络请求
|
||||
MOGO_NETWORK_VERSION=1.4.7.62
|
||||
MOGO_NETWORK_VERSION=1.4.7.65
|
||||
# 鉴权
|
||||
MOGO_PASSPORT_VERSION=1.4.7.62
|
||||
MOGO_PASSPORT_VERSION=1.4.7.65
|
||||
# 常链接
|
||||
MOGO_SOCKET_VERSION=1.4.7.62
|
||||
MOGO_SOCKET_VERSION=1.4.7.65
|
||||
# 数据采集
|
||||
MOGO_REALTIME_VERSION=1.4.7.62
|
||||
MOGO_REALTIME_VERSION=1.4.7.65
|
||||
# 探路,道路事件发布,获取
|
||||
MOGO_TANLU_VERSION=1.4.7.62
|
||||
MOGO_TANLU_VERSION=1.4.7.65
|
||||
# 直播推流
|
||||
MOGO_LIVE_VERSION=1.4.7.62
|
||||
MOGO_LIVE_VERSION=1.4.7.65
|
||||
# 直播拉流
|
||||
MOGO_TRAFFICLIVE_VERSION=1.4.7.62
|
||||
MOGO_TRAFFICLIVE_VERSION=1.4.7.65
|
||||
# 定位服务
|
||||
MOGO_LOCATION_VERSION=1.4.7.62
|
||||
MOGO_LOCATION_VERSION=1.4.7.65
|
||||
# 远程通讯模块
|
||||
MOGO_TELEMATIC_VERSION=1.4.7.62
|
||||
MOGO_TELEMATIC_VERSION=1.4.7.65
|
||||
# v2x
|
||||
MOGO_V2X_VERSION=1.4.7.62
|
||||
# SKIN
|
||||
@@ -65,6 +65,11 @@ SDK_NATIVE_VERSION=1.4.7.49.18-debug
|
||||
UPLOAD_PLUGIN_VERSION=1.4.7.49.18-debug
|
||||
|
||||
|
||||
TTS_BASE_VERSION=1.0.0.1
|
||||
TTS_MOGO_VERSION=1.0.0.1
|
||||
TTS_IFLYTEK_OFFLINE_VERSION=1.0.0.1
|
||||
|
||||
|
||||
|
||||
######################################################################
|
||||
# for vanniktech
|
||||
@@ -75,7 +80,7 @@ SONATYPE_HOST=S01
|
||||
RELEASE_SIGNING_ENABLED=false
|
||||
# 组织名
|
||||
GROUP=com.mogo.cloud
|
||||
VERSION_NAME=1.4.7.62
|
||||
VERSION_NAME=1.4.7.64
|
||||
# 主页
|
||||
POM_URL=https://gitlab.zhidaoauto.com/SCA/L4HA/AndroidApp/support/countly-sdk-android
|
||||
# 版本控制信息
|
||||
|
||||
1
libraries/mogo-tts/app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
7
libraries/mogo-tts/app/EB5/a_keystore_make_eb5.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
# 将 platform.pk8 生成 platform.pem
|
||||
openssl pkcs8 -inform DER -nocrypt -in ./platform.pk8 -out ./platform.pem
|
||||
# platform.x509.pe m生成 platform.p12
|
||||
openssl pkcs12 -export -in ./platform.x509.pem -inkey ./platform.pem -out ./platform.p12 -password pass:android -name eb5_system
|
||||
# 使用keytool工具,通过指令生成keystore
|
||||
keytool -importkeystore -deststorepass password -destkeystore ./eb5_system.keystore -srckeystore ./platform.p12 -srcstoretype PKCS12 -srcstorepass android
|
||||
|
||||
BIN
libraries/mogo-tts/app/EB5/eb5_system.keystore
Normal file
BIN
libraries/mogo-tts/app/EB5/platform.p12
Normal file
28
libraries/mogo-tts/app/EB5/platform.pem
Normal file
@@ -0,0 +1,28 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCceAWSrA1dOBze
|
||||
qmXsyKYAbjZIDG1yB7EgEb5Qhjqr4rVdAJrfcUbW8iAigMfNTXvbJiQ7ioBsJrNL
|
||||
E3UjpJJoIkkE3AFJPnwKzxoFyHT2mwN7YDCdkHTSQoDha60qhzQ2GVHq9ypILQmy
|
||||
BLGHXhKsmMGqdz1oALnq/eVtWL7Y6NoW+aNgCZw3qDSm3+23trRKBJ4Homn8zyxU
|
||||
lvLPNtZN+Qo7jY80o7qrTPUzcasncZs7pYdUrQxT/BTh20XVHiNPu+k8m6Tt+c5U
|
||||
JhNQ7FNWB79pov9KoH219+ogDQmmwbSeIUAvie0RkIk6q1qRgPFS6C+FpFdTz1/B
|
||||
kHHF7sgnAgEDAoIBAGhQA7cdXj4laJRxmUiFxABJeYVdnkwFIMAL1DWu0cfseOir
|
||||
EepLhI9MFWxV2ojeUpIZbX0HAEgZzNy3o20YYZrBhgM9VjDUUrHfZq6Fo08SAlJA
|
||||
IGkK+IwsVeudHhxaIs67i/H6HDAeBnaty6+UDHMQgRxPfkVV0UdT7kjl1JCaMTTc
|
||||
hXxr+oGsH5d5EHCPBfEU4v8BqOBSEgkUFJ1Y6Y5AQqbSe2nSZ6bKZpE8YY27Ipvq
|
||||
BrZEN0j7SKcHKuJDm81DGfLrh3vOmW6U0ZWy7o9qv7INYXNDvtKUMQigXYYANKUp
|
||||
KvveUSgKJ+ixfFP8Ye92j8mp+d/+16i4Wa7Wu5sCgYEAyCd3wu4wsRAu/8q1abL1
|
||||
4/V8M95+dlfhJBl3H9VwUbsH+CP2VRQ9KYkaYdPwOb8hz1h8gqr74ASbEvz+ixMH
|
||||
zI6zel07VDCew+6OUZOBiOxGc8p7SpZuCeDsL+GEZxkOMQ7PXUpDI/p8nx84eEJU
|
||||
ZrHc2kv1VcpdAXLhtrpiicUCgYEAyCA3GDc282T3BXj061vqR2rpFaqhdlRy7S2B
|
||||
tA3BJj2Qrh1bgQGq9agC+YWm3xO3F/lJ+phK2WHoFOpR+m6w7Q0jx/L/BYOlJD+4
|
||||
h1zeZJKhLBQToodM1ZktcLGYFkSzNJeUImt5T1sErj5op9Ex97nZfYswCrl0GtGa
|
||||
NIVKJPsCgYEAhW+lLJ7LILV0qocjm8yj7U5SzT7++Y/rbWZPao5K4SdapW1O42LT
|
||||
cQYRlo1K0SoWijr9rHH9QAMSDKipsgyv3bR3pujSOCBp1/Re4Q0BBfLZoob83GRJ
|
||||
W+tIH+utmhC0ILSKPjGCF/xTFL96+tbi7yE95t1OOTGTVkyWedGXBoMCgYEAhWrP
|
||||
ZXokokNPWPtN8j1G2kdGDnHA+Y2h83OrzV6AxCkLHr49AKvHTnAB+65vP2J6D/uG
|
||||
pxAx5kFFY0bhUZ8gngjChUyqA60YwtUlr5M+7bcWHWK3wa+IjmYeSyEQDth3eGUN
|
||||
bEemNOdYdCmbGot2pSaQ/lzKsdD4EeEReFjcGKcCgYA+JeOt5WFENpv4LT+7P+j1
|
||||
k3xvOZ9sJGuXRXk9HXzsJvRCnc5oScwqku6i5HjzG8gyNVZg1sQGbVbWWmcNtaS8
|
||||
I3XalYAHYQyb8SGxlQP4ctKAN4j2Hbk1OHAMW84dfAQYQwFcBdaJTtMXQlbbX5Rh
|
||||
x90wE4qFuIapx6IKOmDxRw==
|
||||
-----END PRIVATE KEY-----
|
||||
BIN
libraries/mogo-tts/app/EB5/platform.pk8
Normal file
27
libraries/mogo-tts/app/EB5/platform.x509.pem
Normal file
@@ -0,0 +1,27 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEqDCCA5CgAwIBAgIJALOZgIbQVs/6MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYD
|
||||
VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
|
||||
VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
|
||||
AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
|
||||
Fw0wODA0MTUyMjQwNTBaFw0zNTA5MDEyMjQwNTBaMIGUMQswCQYDVQQGEwJVUzET
|
||||
MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
|
||||
A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
|
||||
ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
|
||||
hvcNAQEBBQADggENADCCAQgCggEBAJx4BZKsDV04HN6qZezIpgBuNkgMbXIHsSAR
|
||||
vlCGOqvitV0Amt9xRtbyICKAx81Ne9smJDuKgGwms0sTdSOkkmgiSQTcAUk+fArP
|
||||
GgXIdPabA3tgMJ2QdNJCgOFrrSqHNDYZUer3KkgtCbIEsYdeEqyYwap3PWgAuer9
|
||||
5W1Yvtjo2hb5o2AJnDeoNKbf7be2tEoEngeiafzPLFSW8s821k35CjuNjzSjuqtM
|
||||
9TNxqydxmzulh1StDFP8FOHbRdUeI0+76TybpO35zlQmE1DsU1YHv2mi/0qgfbX3
|
||||
6iANCabBtJ4hQC+J7RGQiTqrWpGA8VLoL4WkV1PPX8GQccXuyCcCAQOjgfwwgfkw
|
||||
HQYDVR0OBBYEFE/koLPdnLop9x1yh8Tnw48ghsKZMIHJBgNVHSMEgcEwgb6AFE/k
|
||||
oLPdnLop9x1yh8Tnw48ghsKZoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
|
||||
CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
|
||||
QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
|
||||
CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJALOZgIbQVs/6MAwGA1Ud
|
||||
EwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBAFclUbjZOh9z3g9tRp+G2tZwFAAp
|
||||
PIigzXzXeLc9r8wZf6t25iEuVsHHYc/EL9cz3lLFCuCIFM78CjtaGkNGBU2Cnx2C
|
||||
tCsgSL+ItdFJKe+F9g7dEtctVWV+IuPoXQTIMdYT0Zk4u4mCJH+jISVroS0dao+S
|
||||
6h2xw3Mxe6DAN/DRr/ZFrvIkl5+6bnoUvAJccbmBOM7z3fwFlhfPJIRc97QNY4L3
|
||||
J17XOElatuWTG5QhdlxJG3L7aOCA29tYwgKdNHyLMozkPvaosVUz7fvpib1qSN1L
|
||||
IC7alMarjdW4OZID2q4u1EYjLk/pvZYTlMYwDlE448/Shebk5INTjLixs1c=
|
||||
-----END CERTIFICATE-----
|
||||
129
libraries/mogo-tts/app/build.gradle
Normal file
@@ -0,0 +1,129 @@
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.android.compileSdkVersion
|
||||
|
||||
signingConfigs {
|
||||
releaseEB5 {
|
||||
keyAlias = 'eb5_system'
|
||||
storeFile file('./EB5/eb5_system.keystore')
|
||||
storePassword 'password'
|
||||
keyPassword 'password'
|
||||
v1SigningEnabled true
|
||||
v2SigningEnabled true
|
||||
}
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.mogo.tts"
|
||||
minSdkVersion rootProject.ext.android.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.android.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
debuggable true
|
||||
minifyEnabled false
|
||||
zipAlignEnabled true
|
||||
shrinkResources false
|
||||
signingConfig signingConfigs.releaseEB5
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
release {
|
||||
debuggable true
|
||||
minifyEnabled false
|
||||
zipAlignEnabled true
|
||||
shrinkResources false
|
||||
signingConfig signingConfigs.releaseEB5
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
||||
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.1'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
|
||||
implementation 'androidx.annotation:annotation:1.2.0'
|
||||
implementation 'androidx.core:core-ktx:1.5.0'
|
||||
|
||||
implementation 'com.blankj:utilcodex:1.30.0'
|
||||
implementation "com.bytedance.boost_multidex:boost_multidex:1.0.1"
|
||||
|
||||
implementation(project(":libraries:mogo-tts:tts-iflytek-offline"))
|
||||
implementation(project(":libraries:mogo-tts:tts-base"))
|
||||
implementation(project(":libraries:mogo-tts:tts-mogo"))
|
||||
|
||||
// ======================集成 Bugly================================================================
|
||||
implementation 'com.tencent.bugly:crashreport:4.1.9.2'
|
||||
// implementation 'androidx.activity:activity:1.13.0'
|
||||
// ======================集成 Bugly================================================================
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//// 打包出来的应用文件名称
|
||||
//android.applicationVariants.all { variant ->
|
||||
// def buildTime = new Date().format("yyyyMMdd", TimeZone.getTimeZone("GMT+08:00"))
|
||||
// def flavor = variant.productFlavors.collect { it.name }.join('-')
|
||||
//
|
||||
// variant.outputs.all { output ->
|
||||
// outputFileName = ["MoGoTTS",
|
||||
// "v${variant.versionName}",
|
||||
// getWorkingBranchName(),
|
||||
// getWorkingBranchHash(),
|
||||
// buildTime,
|
||||
// flavor.length() > 0 ? "[${flavor}]" : "",
|
||||
// variant.buildType.name].findAll { it.length() > 0 }.join('_') << ".apk"
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//// 获取当前分支名称
|
||||
//def getWorkingBranchName() {
|
||||
// def workingBranchName = ""
|
||||
// def proc = "git rev-parse --abbrev-ref HEAD".execute()
|
||||
// proc.in.eachLine { line -> workingBranchName = line }
|
||||
// proc.err.eachLine { line -> println line }
|
||||
// proc.waitFor()
|
||||
// println "Working branch name: " + workingBranchName
|
||||
// return workingBranchName
|
||||
//}
|
||||
//
|
||||
//// 获取当前分支hash
|
||||
//def getWorkingBranchHash() {
|
||||
// def workingBranchHash = ""
|
||||
// def proc = "git log -n1 --format=format:%h".execute()
|
||||
// proc.in.eachLine { line -> workingBranchHash = line }
|
||||
// proc.err.eachLine { line -> println line }
|
||||
// proc.waitFor()
|
||||
// println "Working branch hash: " + workingBranchHash
|
||||
// return workingBranchHash
|
||||
//}
|
||||
//
|
||||
//// 当前构建时间
|
||||
//static def getBuildTime() {
|
||||
// def buildTimeFormat = "yyyy-MM-dd HH:mm:ss"
|
||||
// //设置时间格式
|
||||
// SimpleDateFormat formatter = new SimpleDateFormat(buildTimeFormat, Locale.getDefault())
|
||||
// //获取当前时间
|
||||
// Date curDate = new Date(System.currentTimeMillis())
|
||||
// def buildTime = formatter.format(curDate)
|
||||
// return "\"${buildTime}\""
|
||||
//}
|
||||
|
||||
33
libraries/mogo-tts/app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /projects/android-sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# 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 *;
|
||||
#}
|
||||
|
||||
# This renames the file name on stack traces from "UnknownSource" to "SourceFile"
|
||||
-renamesourcefileattribute SourceFile
|
||||
# This values are kept on stack traces to debug any issue easily
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# Rules recommended for Huawei PushKit
|
||||
-ignorewarnings
|
||||
-keepattributes *Annotation*
|
||||
-keepattributes Exceptions
|
||||
-keepattributes InnerClasses
|
||||
-keepattributes Signature
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
-keep class com.hianalytics.android.**{*;}
|
||||
-keep class com.huawei.updatesdk.**{*;}
|
||||
-keep class com.huawei.hms.**{*;}
|
||||
6
libraries/mogo-tts/app/readme.md
Normal file
@@ -0,0 +1,6 @@
|
||||
### Sample Android app for demo
|
||||
|
||||
This is an example app that sends data to a remote Countly server.
|
||||
|
||||
In order to build and run this application, just open up the project in Android Studio and hit run with an appropriate application.
|
||||
|
||||
45
libraries/mogo-tts/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,45 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.tts">
|
||||
<!-- 允许后台启动服务 -->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.RESTART_PACKAGES" /> <!-- 系统级权限:绕过后台服务限制 -->
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- 关键:允许后台启动 Service & 前台服务 -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_*" /> <!-- 系统签名专用:豁免后台限制 -->
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
|
||||
<application
|
||||
android:name=".MoGoApplication"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:label="@string/app_name"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:persistent="true"
|
||||
android:theme="@style/AppTheme">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<service
|
||||
android:name=".TTSService"
|
||||
android:exported="true"
|
||||
android:process=":ttsP"
|
||||
>
|
||||
<intent-filter>
|
||||
<action android:name="com.mogo.tts.ttsservice" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.mogo.tts
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.appcompat.widget.AppCompatButton
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import com.mogo.tts.common.IMogoTTSCallback
|
||||
import com.mogo.tts.common.log.TtsLogManager
|
||||
import kotlin.random.Random
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
private val ttsManager = TtsManager()
|
||||
|
||||
private val TAG = "MainActivity"
|
||||
|
||||
private val ranx = Random(10)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
|
||||
val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
|
||||
insets
|
||||
}
|
||||
|
||||
findViewById<AppCompatButton>(R.id.actv_test_tts1).setOnClickListener {
|
||||
//TtsInnerManager.speakTTSVoice("此时每个单兵要负责自己的一个视线防区")
|
||||
val info = "此时每个单兵要负责自己的一个视线防区${ranx.nextInt()}"
|
||||
TtsLogManager.d(TAG,"要播放的语音:${info}")
|
||||
ttsManager.speakTTSVoice(info,object :IMogoTTSCallback{
|
||||
override fun onSpeakStart(speakText: String?) {
|
||||
TtsLogManager.d(TAG,"onSpeakStart:${speakText}")
|
||||
}
|
||||
|
||||
override fun onSpeakComple(speakText: String?) {
|
||||
TtsLogManager.d(TAG,"onSpeakComple:${speakText}")
|
||||
}
|
||||
|
||||
override fun onStopTts(speakText: String?) {
|
||||
TtsLogManager.d(TAG,"onStopTts:${speakText}")
|
||||
}
|
||||
|
||||
override fun onSpeakError(speakText: String?, errorMsg: String?) {
|
||||
TtsLogManager.d(TAG,"onSpeakError:${speakText}")
|
||||
}
|
||||
})
|
||||
}
|
||||
findViewById<AppCompatButton>(R.id.actv_test_tts2).setOnClickListener {
|
||||
ttsManager.stopTTS()
|
||||
}
|
||||
|
||||
ttsManager.bindService(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.mogo.tts
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import com.blankj.utilcode.util.DeviceUtils
|
||||
import com.blankj.utilcode.util.FileIOUtils
|
||||
import com.blankj.utilcode.util.PathUtils
|
||||
import com.blankj.utilcode.util.TimeUtils
|
||||
import com.bytedance.boost_multidex.BoostMultiDex
|
||||
import com.mogo.tts.utils.PhoneUtilsExtend
|
||||
import com.tencent.bugly.crashreport.CrashReport
|
||||
import com.tencent.bugly.crashreport.CrashReport.CrashHandleCallback
|
||||
import com.tencent.bugly.crashreport.CrashReport.UserStrategy
|
||||
|
||||
class MoGoApplication : Application() {
|
||||
val TAG = "MoGoApplication"
|
||||
|
||||
override fun attachBaseContext(base: Context) {
|
||||
super.attachBaseContext(base)
|
||||
Log.d(TAG, "--->>>MoGo APP :attachBaseContext")
|
||||
BoostMultiDex.install(base)
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.d(TAG, "--->>>MoGo APP 应用商店启动:onCreate")
|
||||
initBugly()
|
||||
initTts()
|
||||
}
|
||||
|
||||
private fun initTts() {
|
||||
TTSService.start(this)
|
||||
}
|
||||
|
||||
|
||||
private fun initBugly() {
|
||||
val strategy = UserStrategy(this)
|
||||
// 通过UserStrategy设置
|
||||
strategy.deviceID = PhoneUtilsExtend.getDevicesSn()
|
||||
// 通过UserStrategy设置
|
||||
strategy.deviceModel = DeviceUtils.getModel()
|
||||
|
||||
// 最新版SDK支持trace文件采集和anr过程中的主线程堆栈信息采集,由于抓取堆栈的系统接口 Thread.getStackTrace 可能造成crash,建议只对少量用户开启
|
||||
// 这里只对Debug模式下的开启
|
||||
if (BuildConfig.DEBUG) {
|
||||
strategy.appChannel = "MoGo_Debug"
|
||||
// 设置anr时是否获取系统trace文件,默认为false
|
||||
strategy.isEnableCatchAnrTrace = true
|
||||
// 设置是否获取anr过程中的主线程堆栈,默认为true
|
||||
strategy.isEnableRecordAnrMainStack = false
|
||||
} else {
|
||||
strategy.appChannel = "MoGo_Debug"
|
||||
}
|
||||
|
||||
// 设置Crash回调,将日志信息本地存储
|
||||
strategy.setCrashHandleCallback(object : CrashHandleCallback() {
|
||||
override fun onCrashHandleStart(
|
||||
crashType: Int, errorType: String,
|
||||
errorMessage: String, errorStack: String
|
||||
): Map<String, String> {
|
||||
val map = LinkedHashMap<String, String>()
|
||||
map["device_sn"] = PhoneUtilsExtend.getDevicesSn()
|
||||
FileIOUtils.writeFileFromString(
|
||||
PathUtils.getExternalAppFilesPath() + "/crash/" + TimeUtils.getNowString() + ".log",
|
||||
"crashType = $crashType \nerrorType = $errorType \nerrorMessage = $errorMessage \nerrorStack = $errorStack"
|
||||
)
|
||||
return map
|
||||
}
|
||||
|
||||
override fun onCrashHandleStart2GetExtraDatas(
|
||||
crashType: Int, errorType: String,
|
||||
errorMessage: String, errorStack: String
|
||||
): ByteArray? {
|
||||
return try {
|
||||
"Extra data.".toByteArray(charset("UTF-8"))
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
CrashReport.initCrashReport(this, strategy)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.mogo.tts.receiver
|
||||
|
||||
import android.content.BroadcastReceiver
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
|
||||
|
||||
/**
|
||||
* 监听系统启动
|
||||
*/
|
||||
class BootReceiver : BroadcastReceiver() {
|
||||
private val TAG = "BootReceiver"
|
||||
|
||||
private val TARGET_PKG: String = "com.mogo.launcher.f"
|
||||
private val mHandler = Handler(Looper.getMainLooper())
|
||||
private var mRetryCount = 0
|
||||
private val MAX_RETRY: Int = 20 // 最多重试 20 次
|
||||
private val DELAY: Int = 500 // 每 500ms 查一次
|
||||
|
||||
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
if (Intent.ACTION_BOOT_COMPLETED == intent.action||Intent.ACTION_LOCKED_BOOT_COMPLETED == intent.action) { //接收BOOT_COMPLETED广播
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.mogo.tts.utils
|
||||
|
||||
import android.os.Build
|
||||
|
||||
/**
|
||||
* 对PhoneUtils扩展一些方法,获取系统的参数
|
||||
*/
|
||||
object PhoneUtilsExtend {
|
||||
|
||||
fun getDevicesSn(): String {
|
||||
return "name"
|
||||
}
|
||||
}
|
||||
BIN
libraries/mogo-tts/app/src/main/res/drawable/ic_message.png
Normal file
|
After Width: | Height: | Size: 167 B |
26
libraries/mogo-tts/app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<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:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/actv_test_tts1"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:text="开始播放"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatButton
|
||||
android:id="@+id/actv_test_tts2"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/actv_test_tts1"
|
||||
android:text="停止播放"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
BIN
libraries/mogo-tts/app/src/main/res/mipmap-hdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
BIN
libraries/mogo-tts/app/src/main/res/mipmap-hdpi/icon.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
libraries/mogo-tts/app/src/main/res/mipmap-mdpi/ic_launcher.webp
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 7.1 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 9.8 KiB |
6
libraries/mogo-tts/app/src/main/res/values/dimens.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<resources>
|
||||
<dimen name="fab_margin">16dp</dimen>
|
||||
|
||||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">16dp</dimen>
|
||||
</resources>
|
||||
10
libraries/mogo-tts/app/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<string name="app_name">TTS Demo</string>
|
||||
<string name="deep_link">Deep Link Test</string>
|
||||
<string name="countly_hannel_name">General Notifications</string>
|
||||
<string name="countly_channel_description"><![CDATA[News & announcements]]></string>
|
||||
<string name="title_activity_another">AnotherActivity</string>
|
||||
<string name="another_activity_text">Another Activity</string>
|
||||
</resources>
|
||||
17
libraries/mogo-tts/app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<resources>
|
||||
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
|
||||
|
||||
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<base-config cleartextTrafficPermitted="true" />
|
||||
</network-security-config>
|
||||
0
libraries/mogo-tts/readme.md
Normal file
1
libraries/mogo-tts/tts-base/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
67
libraries/mogo-tts/tts-base/README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
### 换肤SDK,
|
||||
#### 使用方式
|
||||
```java
|
||||
public class MoGoApplication extends MultiDexApplication {
|
||||
private static final String TAG = "MoGoApplication";
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// 初始化动态换肤SDK
|
||||
SkinManager.init(this);
|
||||
}
|
||||
}
|
||||
```
|
||||
##### 基础控件0改动完成资源替换,只需要保持使用的「资源文件:drawable、string、color等」皮肤包与APP中保持一致即可
|
||||
TextView 控件的静态文字一定要用string.xml 的方式引用文字显示
|
||||
例如:
|
||||
```xml
|
||||
<TextView
|
||||
android:id="@+id/tvDevicesId"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:text="@string/devicesId"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textSize="20dp" />
|
||||
```
|
||||
##### 如果代码中动态控制了 ImageView 的 src、background 一定要在 XML 中替换成 SkinImageView.java
|
||||
|
||||
##### 代码中动态获取资源设置:图片、文字、颜色
|
||||
```java
|
||||
// 获取颜色
|
||||
SkinResources.getInstance().getColor(resId);
|
||||
|
||||
// 获取图片
|
||||
SkinResources.getInstance().getDrawable(resId);
|
||||
|
||||
// 获取文字
|
||||
SkinResources.getInstance().getString(resId);
|
||||
|
||||
// 获取指定的资源id
|
||||
SkinResources.getInstance().getIdentifier(resId);
|
||||
|
||||
// 获取指定的Raw资源 InputStream
|
||||
SkinResources.getInstance().getRawInputStream(resId);
|
||||
|
||||
// 获取指定的Raw资源的byte[]
|
||||
SkinResources.getInstance().getRawResourceBytes(resId);
|
||||
|
||||
```
|
||||
|
||||
#### 手动控制切换皮肤
|
||||
```java
|
||||
Skin skin = new Skin(
|
||||
// 皮肤包 MD5 用于文件损坏校验的
|
||||
"d5493244467d3970834e42dc1a6f07c9",
|
||||
// 皮肤文件名称
|
||||
"app-skin-debug.skin",
|
||||
// 外网可访问的文件服务器地址
|
||||
"https://carlife-static-1255510688.cos.ap-beijing.myqcloud.com/MoGoEagleEye/app-skin-debug.skin");
|
||||
|
||||
// 换肤
|
||||
SkinManager.getInstance().selectSkin(this, skin);
|
||||
|
||||
// 还原
|
||||
SkinManager.getInstance().loadSkin("");
|
||||
```
|
||||
39
libraries/mogo-tts/tts-base/build.gradle
Normal file
@@ -0,0 +1,39 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.android.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.android.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.android.targetSdkVersion
|
||||
|
||||
versionCode 1
|
||||
versionName "${MOGO_REALTIME_VERSION}"
|
||||
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation rootProject.ext.dependencies.androidxappcompat
|
||||
|
||||
api "com.elegant.utils:common-utils:1.0.58"
|
||||
|
||||
}
|
||||
|
||||
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()
|
||||
0
libraries/mogo-tts/tts-base/consumer-rules.pro
Normal file
4
libraries/mogo-tts/tts-base/gradle.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
GROUP=com.mogo.cloud
|
||||
POM_ARTIFACT_ID=ttsbase
|
||||
VERSION_CODE=1
|
||||
VERSION_NAME=1.0.1-SNAPSHOT
|
||||
21
libraries/mogo-tts/tts-base/proguard-rules.pro
vendored
Normal file
@@ -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
|
||||
9
libraries/mogo-tts/tts-base/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.tts.common">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.mogo.tts;
|
||||
|
||||
import com.mogo.tts.ITTSCallbackBinder;
|
||||
|
||||
interface ICoreBinder {
|
||||
|
||||
void speakTTSVoice1(String tts);
|
||||
|
||||
void speakTTSVoice2(String tts,in ITTSCallbackBinder callback);
|
||||
|
||||
void speakTTSVoic3(String tts, int languageType,in ITTSCallbackBinder callback);
|
||||
|
||||
void registerTtsListener(String tag,in ITTSCallbackBinder callback);
|
||||
|
||||
void unRegisterTtsListener(String tag);
|
||||
|
||||
void clearTtsListener();
|
||||
|
||||
void stopTts();
|
||||
|
||||
void stopTtsByText(String tts);
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.mogo.tts;
|
||||
|
||||
|
||||
oneway interface ITTSCallbackBinder {
|
||||
void onSpeakStart(String speakText);
|
||||
void onSpeakComple(String speakText);
|
||||
void onStopTts(String speakText);
|
||||
void onSpeakError(String speakText,String errorMsg);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,254 @@
|
||||
package com.mogo.tts
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.mogo.tts.common.IMogoTTSCallback
|
||||
import com.mogo.tts.common.LangTtsEntity
|
||||
import com.mogo.tts.common.R
|
||||
import com.mogo.tts.common.log.TtsLogManager
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
class TTSService : Service() {
|
||||
|
||||
private val TAG = "TTSService"
|
||||
|
||||
private val globalStart = "global_"
|
||||
|
||||
// 🔥 最佳方案:ConcurrentHashMap 管理回调(key = tag,value = 回调)
|
||||
private val callbackMap = ConcurrentHashMap<String, ITTSCallbackBinder>()
|
||||
|
||||
|
||||
private val iCorebinder = object : ICoreBinder.Stub() {
|
||||
override fun speakTTSVoice1(tts: String?) {
|
||||
// 防御空字符串
|
||||
val text = tts?.takeIf { it.isNotEmpty() } ?: return
|
||||
TtsInnerManager.speakTTSVoice(tts)
|
||||
}
|
||||
|
||||
override fun speakTTSVoice2(tts: String?, callback: ITTSCallbackBinder?) {
|
||||
// 防御空字符串
|
||||
val text = tts?.takeIf { it.isNotEmpty() } ?: return
|
||||
if(callback==null){
|
||||
TtsInnerManager.speakTTSVoice(tts)
|
||||
return
|
||||
}
|
||||
// 先缓存 callback
|
||||
callbackMap[text] = callback
|
||||
|
||||
TtsInnerManager.speakTTSVoice(tts,object :IMogoTTSCallback{
|
||||
override fun onSpeakStart(speakText: String?) {
|
||||
val cb = callbackMap[speakText] ?: return
|
||||
safeCallback {
|
||||
cb.onSpeakStart(speakText)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSpeakComple(speakText: String?){
|
||||
val cb = callbackMap[speakText] ?: return
|
||||
safeCallback {
|
||||
cb.onSpeakComple(speakText)
|
||||
}
|
||||
// ✅ 执行完自动销毁 callback
|
||||
callbackMap.remove(speakText)
|
||||
}
|
||||
|
||||
override fun onStopTts(speakText: String?) {
|
||||
val cb = callbackMap[speakText] ?: return
|
||||
safeCallback {
|
||||
cb.onStopTts(speakText)
|
||||
}
|
||||
// ✅ 执行完自动销毁 callback
|
||||
callbackMap.remove(speakText)
|
||||
}
|
||||
|
||||
override fun onSpeakError(speakText: String?, errorMsg: String?) {
|
||||
val cb = callbackMap[speakText] ?: return
|
||||
safeCallback {
|
||||
cb.onSpeakError(speakText,errorMsg)
|
||||
}
|
||||
// ✅ 执行完自动销毁 callback
|
||||
callbackMap.remove(speakText)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun speakTTSVoic3(tts: String?, languageType: Int, callback: ITTSCallbackBinder?) {
|
||||
// 防御空字符串
|
||||
val text = tts?.takeIf { it.isNotEmpty() } ?: return
|
||||
if(callback==null){
|
||||
TtsInnerManager.speakTTSVoice(tts)
|
||||
return
|
||||
}
|
||||
// 先缓存 callback
|
||||
callbackMap[text] = callback
|
||||
|
||||
val langTtsEntity = LangTtsEntity.getLangTtsEntity(tts, languageType)
|
||||
|
||||
TtsInnerManager.speakTTSVoice(langTtsEntity,object :IMogoTTSCallback{
|
||||
override fun onSpeakStart(speakText: String?) {
|
||||
val cb = callbackMap[speakText] ?: return
|
||||
safeCallback {
|
||||
cb.onSpeakStart(speakText)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSpeakComple(speakText: String?) {
|
||||
val cb = callbackMap[speakText] ?: return
|
||||
safeCallback {
|
||||
cb.onSpeakComple(speakText)
|
||||
}
|
||||
// ✅ 执行完自动销毁 callback
|
||||
callbackMap.remove(speakText)
|
||||
}
|
||||
|
||||
override fun onStopTts(speakText: String?) {
|
||||
val cb = callbackMap[speakText] ?: return
|
||||
safeCallback {
|
||||
cb.onStopTts(speakText)
|
||||
}
|
||||
// ✅ 执行完自动销毁 callback
|
||||
callbackMap.remove(speakText)
|
||||
}
|
||||
|
||||
override fun onSpeakError(speakText: String?, errorMsg: String?) {
|
||||
val cb = callbackMap[speakText] ?: return
|
||||
safeCallback {
|
||||
cb.onSpeakError(speakText,errorMsg)
|
||||
}
|
||||
// ✅ 执行完自动销毁 callback
|
||||
callbackMap.remove(speakText)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun registerTtsListener(tag: String?, callback: ITTSCallbackBinder?) {
|
||||
if(tag.isNullOrEmpty()){
|
||||
return
|
||||
}
|
||||
if(callback==null){
|
||||
return
|
||||
}
|
||||
val key = "${globalStart}${tag}"
|
||||
callbackMap[key] = callback
|
||||
}
|
||||
|
||||
override fun unRegisterTtsListener(tag: String?) {
|
||||
if(tag.isNullOrEmpty()){
|
||||
return
|
||||
}
|
||||
val key = "${globalStart}${tag}"
|
||||
callbackMap.remove(key)
|
||||
}
|
||||
|
||||
override fun clearTtsListener() {
|
||||
callbackMap.clear()
|
||||
}
|
||||
|
||||
override fun stopTts() {
|
||||
TtsInnerManager.stopTTS()
|
||||
}
|
||||
|
||||
override fun stopTtsByText(tts: String?) {
|
||||
// TODO: 如果正在播放直接停止、如果在排队播放从队伍中移除
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
companion object{
|
||||
fun start(context: Context) {
|
||||
val serviceIntent = Intent(context, TTSService::class.java)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
context.startForegroundService(serviceIntent)
|
||||
} else {
|
||||
context.startService(serviceIntent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
TtsLogManager.d(TAG,"onCreate")
|
||||
TtsInnerManager.init(applicationContext)
|
||||
TtsInnerManager.registerTtsListener(object :IMogoTTSCallback{
|
||||
override fun onSpeakStart(speakText: String?) {
|
||||
callbackMap.forEach {
|
||||
if (it.key.startsWith(globalStart)) {
|
||||
safeCallback {
|
||||
it.value.onSpeakStart(speakText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onSpeakComple(speakText: String?) {
|
||||
callbackMap.forEach {
|
||||
if (it.key.startsWith(globalStart)) {
|
||||
safeCallback {
|
||||
it.value.onSpeakComple(speakText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSpeakError(speakText: String?, errorMsg: String) {
|
||||
callbackMap.forEach {
|
||||
if (it.key.startsWith(globalStart)) {
|
||||
safeCallback {
|
||||
it.value.onSpeakError(speakText,errorMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
TtsLogManager.d(TAG,"onBind")
|
||||
return iCorebinder
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
TtsLogManager.d(TAG,"onStartCommand")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val manager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
if (manager != null) {
|
||||
val nc = NotificationChannel("Voice", "语音服务", NotificationManager.IMPORTANCE_HIGH)
|
||||
manager.createNotificationChannel(nc)
|
||||
}
|
||||
}
|
||||
val notification: Notification = NotificationCompat.Builder(applicationContext, "Voice")
|
||||
.setContentTitle("语音服务运行中")
|
||||
.setSmallIcon(R.drawable.voice_manager_icon)
|
||||
.build()
|
||||
|
||||
startForeground(1, notification)
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
TtsLogManager.d(TAG,"onDestroy")
|
||||
super.onDestroy()
|
||||
TtsInnerManager.unRegisterTtsListener()
|
||||
TtsInnerManager.release()
|
||||
}
|
||||
|
||||
|
||||
// 跨进程回调安全工具方法
|
||||
private inline fun safeCallback(call: () -> Unit) {
|
||||
try {
|
||||
call()
|
||||
} catch (e: Exception) {
|
||||
TtsLogManager.e(TAG,e.toString(),e)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.mogo.tts
|
||||
|
||||
import android.content.Context
|
||||
import com.mogo.tts.common.IMogoTTS
|
||||
import com.mogo.tts.common.IMogoTTSCallback
|
||||
import com.mogo.tts.common.LangTtsEntity
|
||||
import com.mogo.tts.common.LanguageType
|
||||
import com.mogo.tts.common.log.TtsLogManager
|
||||
|
||||
object TtsInnerManager {
|
||||
|
||||
private const val TAG = "TtsManager"
|
||||
|
||||
private var mTTS: IMogoTTS? = null
|
||||
|
||||
fun init(context: Context) {
|
||||
try {
|
||||
// 暂时换成反射,解决死锁问题
|
||||
var clazz1: Class<*>? = null
|
||||
try {
|
||||
// clazz1 = Class.forName("com.mogo.tts.iflytek.offline.IFlyTekOfflineTts")
|
||||
clazz1 = Class.forName("com.k2fsa.sherpa.onnx.MogoOfflineTTS")
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
|
||||
if (clazz1 != null) {
|
||||
mTTS = clazz1.getConstructor().newInstance() as IMogoTTS
|
||||
}
|
||||
mTTS?.initTts(context)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
TtsLogManager.d(TAG, "TTS 模块初始化异常")
|
||||
}
|
||||
}
|
||||
|
||||
fun speakTTSVoice(tts: String?) {
|
||||
if(tts.isNullOrEmpty()) return
|
||||
speakTTSVoice(tts,null)
|
||||
}
|
||||
|
||||
|
||||
fun speakTTSVoice(tts: String?, callBack: IMogoTTSCallback?) {
|
||||
if(tts.isNullOrEmpty()) return
|
||||
speakTTSVoice(LangTtsEntity(tts, LanguageType.CHINESE), callBack);
|
||||
}
|
||||
|
||||
fun speakTTSVoice(ttsEntity: LangTtsEntity, callBack: IMogoTTSCallback?) {
|
||||
if(ttsEntity.ttsContent.isEmpty()){
|
||||
return
|
||||
}
|
||||
mTTS?.speakTTSVoice(ttsEntity, callBack)
|
||||
}
|
||||
|
||||
fun registerTtsListener(callback: IMogoTTSCallback) {
|
||||
mTTS?.registerTtsListener(callback)
|
||||
}
|
||||
|
||||
fun unRegisterTtsListener() {
|
||||
mTTS?.unRegisterTtsListener()
|
||||
}
|
||||
|
||||
fun stopTTS() {
|
||||
mTTS?.stopTts()
|
||||
}
|
||||
|
||||
fun release() {
|
||||
mTTS?.release()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package com.mogo.tts
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.RemoteException
|
||||
import com.elegant.utils.UiThreadHandler
|
||||
import com.mogo.tts.common.IMogoTTSCallback
|
||||
import com.mogo.tts.common.LangTtsEntity
|
||||
import com.mogo.tts.common.LanguageType
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
|
||||
class TtsManager {
|
||||
companion object {
|
||||
private const val SERVER_PACKAGE: String = "com.mogo.tts"
|
||||
}
|
||||
|
||||
private val isBind = AtomicBoolean(false)
|
||||
private val isServiceException = AtomicBoolean(false) //服务绑定成功后出现异常
|
||||
protected var binder: ICoreBinder? = null
|
||||
|
||||
private var context: Context? = null
|
||||
|
||||
fun speakTTSVoice(tts: String?) {
|
||||
if(tts.isNullOrEmpty()) return
|
||||
speakTTSVoice(LangTtsEntity(tts, LanguageType.CHINESE), null);
|
||||
}
|
||||
|
||||
|
||||
fun speakTTSVoice(tts: String?, callBack: IMogoTTSCallback?) {
|
||||
if(tts.isNullOrEmpty()) return
|
||||
speakTTSVoice(LangTtsEntity(tts, LanguageType.CHINESE), callBack);
|
||||
}
|
||||
|
||||
fun speakTTSVoice(ttsEntity: LangTtsEntity, callBack: IMogoTTSCallback?) {
|
||||
if(ttsEntity.ttsContent.isEmpty()){
|
||||
return
|
||||
}
|
||||
if(binder!=null){
|
||||
try {
|
||||
binder?.speakTTSVoic3(ttsEntity.ttsContent,ttsEntity.getLanguageDef(),object :
|
||||
ITTSCallbackBinder.Stub() {
|
||||
override fun onSpeakStart(speakText: String?) {
|
||||
callBack?.onSpeakStart(speakText)
|
||||
}
|
||||
|
||||
override fun onSpeakComple(speakText: String?) {
|
||||
callBack?.onSpeakComple(speakText)
|
||||
}
|
||||
|
||||
override fun onStopTts(speakText: String?) {
|
||||
callBack?.onStopTts(speakText)
|
||||
}
|
||||
|
||||
override fun onSpeakError(speakText: String?, errorMsg: String?) {
|
||||
callBack?.onSpeakError(speakText,errorMsg)
|
||||
}
|
||||
})
|
||||
} catch (e: RemoteException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun registerTtsListener(tag:String,callback: IMogoTTSCallback) {
|
||||
if(binder!=null){
|
||||
try {
|
||||
binder?.registerTtsListener(tag,object :ITTSCallbackBinder.Stub(){
|
||||
override fun onSpeakStart(speakText: String?) {
|
||||
callback.onSpeakStart(speakText)
|
||||
}
|
||||
|
||||
override fun onSpeakComple(speakText: String?) {
|
||||
callback.onSpeakComple(speakText)
|
||||
}
|
||||
|
||||
override fun onStopTts(speakText: String?) {
|
||||
callback.onStopTts(speakText)
|
||||
}
|
||||
|
||||
override fun onSpeakError(speakText: String?, errorMsg: String?) {
|
||||
callback.onSpeakError(speakText,errorMsg)
|
||||
}
|
||||
})
|
||||
} catch (e: RemoteException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun unRegisterTtsListener(tag: String) {
|
||||
if(binder!=null){
|
||||
try {
|
||||
binder?.unRegisterTtsListener(tag)
|
||||
} catch (e: RemoteException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun stopTTS() {
|
||||
if(binder!=null){
|
||||
try {
|
||||
binder?.stopTts()
|
||||
} catch (e: RemoteException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun stopTTS(tts:String) {
|
||||
if(binder!=null){
|
||||
try {
|
||||
binder?.stopTtsByText(tts)
|
||||
} catch (e: RemoteException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val connection = object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
binder = ICoreBinder.Stub.asInterface(service);
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
isServiceException.set(true);
|
||||
unbind()
|
||||
delayBind()
|
||||
}
|
||||
}
|
||||
|
||||
fun bindService(
|
||||
context: Context,
|
||||
) {
|
||||
this.context = context
|
||||
bindService()
|
||||
}
|
||||
|
||||
private fun bindService() {
|
||||
context?.let {
|
||||
if (!isBind.get()) {
|
||||
if (isServerInstalled()) {
|
||||
isBind.set(true)
|
||||
val intent = Intent("com.mogo.tts.ttsservice")
|
||||
intent.setPackage(SERVER_PACKAGE)
|
||||
val isSuccess: Boolean =
|
||||
it.bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
||||
if (!isSuccess) {
|
||||
unbind()
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unbind() {
|
||||
if (binder != null) {
|
||||
try {
|
||||
binder?.clearTtsListener()
|
||||
} catch (e: RemoteException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
binder = null
|
||||
}
|
||||
context?.let {
|
||||
if (isBind.get()) {
|
||||
isBind.set(false)
|
||||
try {
|
||||
it.unbindService(connection)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun delayBind() {
|
||||
if (isServiceException.get()) {
|
||||
isServiceException.set(false)
|
||||
UiThreadHandler.postDelayed({
|
||||
bindService()
|
||||
}, 4000L)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isServerInstalled(): Boolean {
|
||||
context?.let {
|
||||
val pm: PackageManager = it.getPackageManager()
|
||||
return try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
pm.getApplicationInfo(
|
||||
SERVER_PACKAGE,
|
||||
PackageManager.ApplicationInfoFlags.of(0)
|
||||
).enabled
|
||||
} else {
|
||||
pm.getApplicationInfo(SERVER_PACKAGE, 0).enabled
|
||||
}
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.mogo.tts.common
|
||||
|
||||
interface IGlobalTtsCallback {
|
||||
fun onTtsSpeakStart(speakText: String?)
|
||||
|
||||
fun onTtsSpeakEnd(speakText: String?)
|
||||
|
||||
fun onTtsSpeakComple(speakText: String?)
|
||||
|
||||
fun onTtsSpeakError(speakText: String?, errorMsg: String)
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.mogo.tts.common;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2020/10/12
|
||||
*
|
||||
* 语音抽象
|
||||
*/
|
||||
interface IMogoTTS {
|
||||
|
||||
void initTts(Context context);
|
||||
|
||||
/**
|
||||
* 释放资源
|
||||
*/
|
||||
void release();
|
||||
|
||||
void registerTtsListener(IMogoTTSCallback callback);
|
||||
|
||||
void unRegisterTtsListener();
|
||||
|
||||
void speakTTSVoice(LangTtsEntity ttsEntity, IMogoTTSCallback callBack);
|
||||
|
||||
void stopTts();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.mogo.tts.common;
|
||||
|
||||
public
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2020/10/12
|
||||
*
|
||||
* 描述
|
||||
*/
|
||||
interface IMogoTTSCallback {
|
||||
|
||||
default void onSpeakStart( String speakText ) {
|
||||
}
|
||||
|
||||
default void onSpeakComple( String speakText ) {
|
||||
}
|
||||
|
||||
default void onStopTts( String speakText ) {
|
||||
}
|
||||
|
||||
default void onSpeakError( String speakText, String errorMsg) {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.mogo.tts.common
|
||||
|
||||
data class LangTtsEntity(
|
||||
var ttsContent: String,
|
||||
var language: LanguageType
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "ttsContent is:$ttsContent,language is:${language.langName}"
|
||||
}
|
||||
|
||||
fun getLanguageDef(): Int {
|
||||
return when (language) {
|
||||
LanguageType.CHINESE -> LanguageTypeDef.MODE_CHINESE
|
||||
LanguageType.ENGLISH -> LanguageTypeDef.MODE_ENGLISH
|
||||
LanguageType.KOREAN -> LanguageTypeDef.MODE_KOREAN
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
fun getLangTtsEntity(tts: String, languageType: Int): LangTtsEntity {
|
||||
val languageType = when (languageType) {
|
||||
LanguageTypeDef.MODE_ENGLISH -> {
|
||||
LanguageType.ENGLISH
|
||||
}
|
||||
|
||||
LanguageTypeDef.MODE_KOREAN -> {
|
||||
LanguageType.KOREAN
|
||||
}
|
||||
|
||||
else -> {
|
||||
LanguageType.CHINESE
|
||||
}
|
||||
}
|
||||
return LangTtsEntity(tts, languageType)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.mogo.tts.common
|
||||
|
||||
enum class LanguageType(val langName: String) {
|
||||
CHINESE("chinese"),
|
||||
ENGLISH("english"),
|
||||
KOREAN("korean")
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.mogo.tts.common;
|
||||
|
||||
import androidx.annotation.IntDef;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface LanguageTypeDef{
|
||||
public static final int MODE_CHINESE = 0;
|
||||
public static final int MODE_ENGLISH = 1;
|
||||
public static final int MODE_KOREAN = 2;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.mogo.tts.common;
|
||||
|
||||
public
|
||||
/**
|
||||
* @author congtaowang
|
||||
* @since 2020/10/12
|
||||
*
|
||||
* 语音打算类型
|
||||
*/
|
||||
enum PreemptType {
|
||||
|
||||
/**
|
||||
* 不打断
|
||||
*/
|
||||
PREEMPT_TYPE_NONE,
|
||||
/**
|
||||
* 立即打断,取消当前的tts插队播放
|
||||
*/
|
||||
PREEMPT_TYPE_IMMEDIATELY,
|
||||
/**
|
||||
* 下一个插入,不取消当前的tts,插队下一个播放
|
||||
*/
|
||||
PREEMPT_TYPE_NEXT,
|
||||
/**
|
||||
* 清空队列
|
||||
*/
|
||||
PREEMPT_TYPE_FLUSH,
|
||||
/**
|
||||
* 立即打断,不取消当前tts
|
||||
*/
|
||||
PREEMPT_TYPE_IMMEDIATELY_WITHOUT_CANCEL
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package com.mogo.tts.common.impl;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.elegant.utils.UiThreadHandler;
|
||||
import com.mogo.tts.common.IMogoTTS;
|
||||
import com.mogo.tts.common.IMogoTTSCallback;
|
||||
import com.mogo.tts.common.LangTtsEntity;
|
||||
import com.mogo.tts.common.log.TtsLogManager;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public abstract class BaseMogoTTS implements IMogoTTS {
|
||||
protected Context context;
|
||||
|
||||
// 由于主动打断不会有回调事件,所以主动打断时清掉map中被打断的text和callback
|
||||
protected volatile String curTtsContent = "";
|
||||
protected volatile LangTtsEntity curTtsEntity = null;
|
||||
|
||||
protected HashMap<String, IMogoTTSCallback> speakVoiceMap = new HashMap<>();
|
||||
protected IMogoTTSCallback mGlobalTtsCallback = null;
|
||||
|
||||
protected String getTAG() {
|
||||
return "BaseMogoTTS";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initTts(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void registerTtsListener(IMogoTTSCallback callback) {
|
||||
this.mGlobalTtsCallback = callback;
|
||||
}
|
||||
|
||||
public void unRegisterTtsListener(){
|
||||
this.mGlobalTtsCallback = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void speakTTSVoice(LangTtsEntity ttsEntity, IMogoTTSCallback callBack) {
|
||||
if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
|
||||
UiThreadHandler.post(() -> {
|
||||
if (callBack != null) {
|
||||
speakVoiceMap.put(ttsEntity.toString(),callBack);
|
||||
}
|
||||
speakMultiLangTTS(ttsEntity);
|
||||
});
|
||||
}else {
|
||||
if (callBack != null) {
|
||||
speakVoiceMap.put(ttsEntity.toString(),callBack);
|
||||
}
|
||||
speakMultiLangTTS(ttsEntity);
|
||||
}
|
||||
}
|
||||
|
||||
protected void speakMultiLangTTS(LangTtsEntity ttsEntity){
|
||||
// 合成并播放
|
||||
TtsLogManager.d(getTAG(), "tts准备合成:"+ttsEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopTts() {
|
||||
if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
|
||||
UiThreadHandler.post(this::realStop);
|
||||
} else {
|
||||
realStop();
|
||||
}
|
||||
}
|
||||
|
||||
protected void realStop() {
|
||||
TtsLogManager.d(getTAG(),"停止tts");
|
||||
if(curTtsEntity!=null){
|
||||
String key = curTtsEntity.toString();
|
||||
if (speakVoiceMap.containsKey(key)) {
|
||||
IMogoTTSCallback remove = speakVoiceMap.remove(key);
|
||||
if(remove!=null) {
|
||||
remove.onStopTts(curTtsEntity.getTtsContent());
|
||||
}
|
||||
}
|
||||
curTtsEntity = null;
|
||||
}
|
||||
this.curTtsContent = "";
|
||||
}
|
||||
|
||||
public void onSpeakBegin() {
|
||||
if(mGlobalTtsCallback != null) {
|
||||
mGlobalTtsCallback.onSpeakStart(curTtsContent);
|
||||
}
|
||||
if (curTtsEntity!=null) {
|
||||
String key = curTtsEntity.toString();
|
||||
IMogoTTSCallback iMogoTTSCallback = speakVoiceMap.get(key);
|
||||
if(iMogoTTSCallback!=null){
|
||||
iMogoTTSCallback.onSpeakStart(curTtsEntity.getTtsContent());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void handleCompleteEvent() {
|
||||
if(mGlobalTtsCallback!=null) {
|
||||
mGlobalTtsCallback.onSpeakComple(curTtsContent);
|
||||
}
|
||||
if (curTtsEntity!=null) {
|
||||
String key = curTtsEntity.toString();
|
||||
IMogoTTSCallback iMogoTTSCallback = speakVoiceMap.get(key);
|
||||
if(iMogoTTSCallback!=null){
|
||||
iMogoTTSCallback.onSpeakComple(curTtsEntity.getTtsContent());
|
||||
}
|
||||
}
|
||||
curTtsEntity = null;
|
||||
curTtsContent = "";
|
||||
}
|
||||
|
||||
public void handleErrorEvent(String error) {
|
||||
if(mGlobalTtsCallback!=null) {
|
||||
mGlobalTtsCallback.onSpeakError(curTtsContent,error);
|
||||
}
|
||||
if (curTtsEntity != null) {
|
||||
String key = curTtsEntity.toString();
|
||||
IMogoTTSCallback iMogoTTSCallback = speakVoiceMap.get(key);
|
||||
if(iMogoTTSCallback!=null){
|
||||
iMogoTTSCallback.onSpeakError(curTtsEntity.getTtsContent(), error);
|
||||
}
|
||||
} else {
|
||||
IMogoTTSCallback iMogoTTSCallback = speakVoiceMap.get(curTtsContent);
|
||||
if(iMogoTTSCallback!=null){
|
||||
iMogoTTSCallback.onSpeakError(curTtsContent, error);
|
||||
}
|
||||
}
|
||||
curTtsEntity = null;
|
||||
curTtsContent = "";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.mogo.tts.common.log;
|
||||
|
||||
public enum LogLevel {
|
||||
info,warn,debug,error
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package com.mogo.tts.common.log;
|
||||
|
||||
public interface LogListener {
|
||||
void writeLog(LogLevel logLevel,String tag,String message);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.mogo.tts.common.log
|
||||
|
||||
import android.util.Log
|
||||
|
||||
object TtsLogManager {
|
||||
|
||||
private var isDebug = true
|
||||
|
||||
private var logListener:LogListener? = null
|
||||
|
||||
fun setDebug(isDebug:Boolean){
|
||||
this.isDebug = isDebug
|
||||
}
|
||||
|
||||
fun setLogListener(logListener: LogListener){
|
||||
this.logListener = logListener
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun d(tag: String, message: String) {
|
||||
if(isDebug) {
|
||||
Log.d(tag, message);
|
||||
}
|
||||
logListener?.writeLog(LogLevel.debug,tag,message)
|
||||
}
|
||||
|
||||
|
||||
@JvmStatic
|
||||
fun i(tag: String, message: String) {
|
||||
if(isDebug) {
|
||||
Log.i(tag, message)
|
||||
}
|
||||
logListener?.writeLog(LogLevel.info,tag,message)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun w(tag: String, message: String) {
|
||||
if(isDebug) {
|
||||
Log.w(tag, message)
|
||||
}
|
||||
logListener?.writeLog(LogLevel.warn,tag,message)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun e(tag: String, message: String) {
|
||||
if(isDebug) {
|
||||
Log.e(tag, message)
|
||||
}
|
||||
logListener?.writeLog(LogLevel.error,tag,message)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun e(tag: String, message: String,tr:Throwable?) {
|
||||
if(isDebug) {
|
||||
Log.e(tag, message,tr)
|
||||
}
|
||||
logListener?.writeLog(LogLevel.error,tag,message)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.mogo.tts.common.utils;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
|
||||
public class FileUtils {
|
||||
public static void copyAssetsToLocal(Context context, String assetDir, String localDir) throws IOException {
|
||||
// 获取assets目录下的所有文件和目录
|
||||
String[] files = context.getAssets().list(assetDir);
|
||||
if (files == null || files.length == 0) {
|
||||
return;
|
||||
}
|
||||
// 创建本地目标目录
|
||||
File localDirFile = new File(localDir);
|
||||
if (!localDirFile.exists()) {
|
||||
localDirFile.mkdirs();
|
||||
}
|
||||
for (String fileName : files) {
|
||||
String assetPath = assetDir + File.separator + fileName;
|
||||
String localPath = localDir + File.separator + fileName;
|
||||
// 如果是文件夹,递归调用
|
||||
if (isDirectory(context, assetPath)) {
|
||||
copyAssetsToLocal(context, assetPath, localPath);
|
||||
} else {
|
||||
try (InputStream in = context.getAssets().open(assetPath);
|
||||
FileOutputStream out = new FileOutputStream(localPath)) {
|
||||
byte[] buffer = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
out.write(buffer, 0, read);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 判断 assets 中的路径是否是文件夹
|
||||
public static boolean isDirectory(Context context, String path) throws IOException {
|
||||
String[] files = context.getAssets().list(path);
|
||||
return files != null && files.length > 0;
|
||||
}
|
||||
|
||||
public static void writeFile(String path, byte[] bytes) {
|
||||
boolean append = false;
|
||||
try {
|
||||
File file = new File(path);
|
||||
if (file.exists()) {
|
||||
append = true;
|
||||
}else {
|
||||
file.createNewFile();
|
||||
}
|
||||
FileOutputStream out = new FileOutputStream(path,true);//指定写到哪个路径中
|
||||
FileChannel fileChannel = out.getChannel();
|
||||
fileChannel.write(ByteBuffer.wrap(bytes)); //将字节流写入文件中
|
||||
fileChannel.force(true);//强制刷新
|
||||
fileChannel.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package com.mogo.tts.utils
|
||||
|
||||
import android.content.Context
|
||||
|
||||
class ApkUtils {
|
||||
companion object{
|
||||
@JvmStatic
|
||||
fun isInstallAppStore(ctx: Context): Boolean {
|
||||
return try {
|
||||
val installedPackages = ctx.packageManager.getInstalledPackages(0)
|
||||
installedPackages.find { it.packageName == "com.mogo.tts" } != null
|
||||
} catch (t: Throwable) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#000000" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
|
||||
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,15c1.66,0 2.99,-1.34 2.99,-3L15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6v6c0,1.66 1.34,3 3,3zM17.3,12c0,3 -2.54,5.1 -5.3,5.1S6.7,15 6.7,12L5,12c0,3.42 2.72,6.23 6,6.72L11,22h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
|
||||
|
||||
</vector>
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<attr name="skinTypeface" format="string" />
|
||||
<!--防止大面积误伤原生View,这里做一个手动开启换肤的开关,来控制是否要在这个控件上使用-->
|
||||
<attr name="isUseSkin" format="boolean" />
|
||||
</resources>
|
||||
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">skin-core2</string>
|
||||
</resources>
|
||||
1
libraries/mogo-tts/tts-iflytek-offline/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
67
libraries/mogo-tts/tts-iflytek-offline/README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
### 换肤SDK,
|
||||
#### 使用方式
|
||||
```java
|
||||
public class MoGoApplication extends MultiDexApplication {
|
||||
private static final String TAG = "MoGoApplication";
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// 初始化动态换肤SDK
|
||||
SkinManager.init(this);
|
||||
}
|
||||
}
|
||||
```
|
||||
##### 基础控件0改动完成资源替换,只需要保持使用的「资源文件:drawable、string、color等」皮肤包与APP中保持一致即可
|
||||
TextView 控件的静态文字一定要用string.xml 的方式引用文字显示
|
||||
例如:
|
||||
```xml
|
||||
<TextView
|
||||
android:id="@+id/tvDevicesId"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:text="@string/devicesId"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textSize="20dp" />
|
||||
```
|
||||
##### 如果代码中动态控制了 ImageView 的 src、background 一定要在 XML 中替换成 SkinImageView.java
|
||||
|
||||
##### 代码中动态获取资源设置:图片、文字、颜色
|
||||
```java
|
||||
// 获取颜色
|
||||
SkinResources.getInstance().getColor(resId);
|
||||
|
||||
// 获取图片
|
||||
SkinResources.getInstance().getDrawable(resId);
|
||||
|
||||
// 获取文字
|
||||
SkinResources.getInstance().getString(resId);
|
||||
|
||||
// 获取指定的资源id
|
||||
SkinResources.getInstance().getIdentifier(resId);
|
||||
|
||||
// 获取指定的Raw资源 InputStream
|
||||
SkinResources.getInstance().getRawInputStream(resId);
|
||||
|
||||
// 获取指定的Raw资源的byte[]
|
||||
SkinResources.getInstance().getRawResourceBytes(resId);
|
||||
|
||||
```
|
||||
|
||||
#### 手动控制切换皮肤
|
||||
```java
|
||||
Skin skin = new Skin(
|
||||
// 皮肤包 MD5 用于文件损坏校验的
|
||||
"d5493244467d3970834e42dc1a6f07c9",
|
||||
// 皮肤文件名称
|
||||
"app-skin-debug.skin",
|
||||
// 外网可访问的文件服务器地址
|
||||
"https://carlife-static-1255510688.cos.ap-beijing.myqcloud.com/MoGoEagleEye/app-skin-debug.skin");
|
||||
|
||||
// 换肤
|
||||
SkinManager.getInstance().selectSkin(this, skin);
|
||||
|
||||
// 还原
|
||||
SkinManager.getInstance().loadSkin("");
|
||||
```
|
||||
44
libraries/mogo-tts/tts-iflytek-offline/build.gradle
Normal file
@@ -0,0 +1,44 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.android.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.android.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.android.targetSdkVersion
|
||||
|
||||
versionCode 1
|
||||
versionName "${MOGO_REALTIME_VERSION}"
|
||||
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
if (Boolean.valueOf(RELEASE)) {
|
||||
implementation "com.mogo.cloud:ttsbase:${TTS_BASE_VERSION}"
|
||||
}else {
|
||||
implementation(project(":libraries:mogo-tts:tts-base"))
|
||||
}
|
||||
|
||||
implementation files('libs/AIKit.aar')
|
||||
implementation rootProject.ext.dependencies.androidxappcompat
|
||||
|
||||
}
|
||||
|
||||
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()
|
||||
4
libraries/mogo-tts/tts-iflytek-offline/gradle.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
GROUP=com.mogo.cloud
|
||||
POM_ARTIFACT_ID=ttsiflytekoffline
|
||||
VERSION_CODE=1
|
||||
VERSION_NAME=1.0.1-SNAPSHOT
|
||||
BIN
libraries/mogo-tts/tts-iflytek-offline/libs/AIKit.aar
Normal file
21
libraries/mogo-tts/tts-iflytek-offline/proguard-rules.pro
vendored
Normal file
@@ -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
|
||||
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.tts.iflytek.offline">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,229 @@
|
||||
package com.mogo.tts.iflytek.offline
|
||||
|
||||
import android.media.AudioFormat
|
||||
import android.media.AudioManager
|
||||
import android.media.AudioRecord
|
||||
import android.media.AudioTrack
|
||||
import android.os.Process
|
||||
import java.io.DataInputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
|
||||
class AudioTrackManager {
|
||||
private var mAudioTrack: AudioTrack? = null
|
||||
private var mDis: DataInputStream? = null //播放文件的数据流
|
||||
private var mRecordThread: Thread? = null
|
||||
private var isStart = false
|
||||
|
||||
//指定缓冲区大小。调用AudioRecord类的getMinBufferSize方法可以获得。
|
||||
private var mMinBufferSize = 0
|
||||
private var mSampleRate = 16000
|
||||
|
||||
private var listener: OnCompleteListener? = null
|
||||
|
||||
enum class sampleRateType {
|
||||
SAMPLE_RATE_16k,
|
||||
SAMPLE_RATE_24k
|
||||
}
|
||||
|
||||
private fun initData() {
|
||||
//根据采样率,采样精度,单双声道来得到frame的大小。
|
||||
mMinBufferSize =
|
||||
AudioTrack.getMinBufferSize(mSampleRate, mChannelConfig, mAudioFormat) //计算最小缓冲区
|
||||
//注意,按照数字音频的知识,这个算出来的是一秒钟buffer的大小。
|
||||
//创建AudioTrack
|
||||
mAudioTrack = AudioTrack(
|
||||
mStreamType, mSampleRate, mChannelConfig,
|
||||
mAudioFormat, mMinBufferSize, mMode
|
||||
)
|
||||
}
|
||||
|
||||
fun setListener(listener: OnCompleteListener) {
|
||||
this.listener = listener
|
||||
}
|
||||
|
||||
fun setSampleRate(sampleRate: sampleRateType?) {
|
||||
when (sampleRate) {
|
||||
sampleRateType.SAMPLE_RATE_16k -> mSampleRate = mSampleRateIn16KHz
|
||||
sampleRateType.SAMPLE_RATE_24k -> mSampleRate = mSampleRateIn24KHz
|
||||
else -> {
|
||||
mSampleRate = mSampleRateIn24KHz
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁线程方法
|
||||
*/
|
||||
private fun destroyThread() {
|
||||
try {
|
||||
isStart = false
|
||||
if (null != mRecordThread && Thread.State.RUNNABLE == mRecordThread!!.state) {
|
||||
try {
|
||||
Thread.sleep(500)
|
||||
mRecordThread!!.interrupt()
|
||||
} catch (e: Exception) {
|
||||
mRecordThread = null
|
||||
}
|
||||
}
|
||||
mRecordThread = null
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
mRecordThread = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动播放线程
|
||||
*/
|
||||
private fun startThread() {
|
||||
destroyThread()
|
||||
isStart = true
|
||||
if (mRecordThread == null) {
|
||||
mRecordThread = Thread(playRunnable)
|
||||
mRecordThread!!.start()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放线程
|
||||
*/
|
||||
var playRunnable: Runnable = Runnable {
|
||||
try {
|
||||
//设置线程的优先级
|
||||
Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO)
|
||||
val tempBuffer = ByteArray(mMinBufferSize)
|
||||
var readCount = 0
|
||||
while (mDis!!.available() > 0) {
|
||||
readCount = mDis!!.read(tempBuffer)
|
||||
if (readCount == AudioTrack.ERROR_INVALID_OPERATION || readCount == AudioTrack.ERROR_BAD_VALUE) {
|
||||
continue
|
||||
}
|
||||
if (readCount != 0 && readCount != -1) { //一边播放一边写入语音数据
|
||||
//判断AudioTrack未初始化,停止播放的时候释放了,状态就为STATE_UNINITIALIZED
|
||||
if (mAudioTrack!!.state == AudioTrack.STATE_UNINITIALIZED) {
|
||||
initData()
|
||||
}
|
||||
mAudioTrack!!.play()
|
||||
mAudioTrack!!.write(tempBuffer, 0, readCount)
|
||||
}
|
||||
}
|
||||
stopPlay() //播放完就停止播放
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
listener?.onComplete()
|
||||
}
|
||||
|
||||
init {
|
||||
initData()
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放文件
|
||||
* @param path
|
||||
* @throws Exception
|
||||
*/
|
||||
@Throws(Exception::class)
|
||||
private fun setPath(path: String) {
|
||||
val file = File(path)
|
||||
mDis = DataInputStream(FileInputStream(file))
|
||||
}
|
||||
|
||||
/**
|
||||
* 启动播放
|
||||
*
|
||||
* @param path
|
||||
*/
|
||||
fun startPlay(path: String) {
|
||||
try {
|
||||
// //AudioTrack未初始化
|
||||
// if(mAudioTrack.getState() == AudioTrack.STATE_UNINITIALIZED){
|
||||
// throw new RuntimeException("The AudioTrack is not uninitialized");
|
||||
// }//AudioRecord.getMinBufferSize的参数是否支持当前的硬件设备
|
||||
// else if (AudioTrack.ERROR_BAD_VALUE == mMinBufferSize || AudioTrack.ERROR == mMinBufferSize) {
|
||||
// throw new RuntimeException("AudioTrack Unable to getMinBufferSize");
|
||||
// }else{
|
||||
setPath(path)
|
||||
startThread()
|
||||
|
||||
// }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止播放
|
||||
*/
|
||||
fun stopPlay() {
|
||||
try {
|
||||
destroyThread() //销毁线程
|
||||
if (mAudioTrack != null) {
|
||||
if (mAudioTrack!!.state == AudioRecord.STATE_INITIALIZED) { //初始化成功
|
||||
mAudioTrack!!.stop() //停止播放
|
||||
}
|
||||
if (mAudioTrack != null) {
|
||||
mAudioTrack!!.release() //释放audioTrack资源
|
||||
}
|
||||
}
|
||||
if (mDis != null) {
|
||||
mDis!!.close() //关闭数据输入流
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
val playState: Int
|
||||
get() {
|
||||
if (mAudioTrack != null) {
|
||||
return mAudioTrack!!.playState
|
||||
}
|
||||
return AudioTrack.PLAYSTATE_STOPPED
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
private var mInstance: AudioTrackManager? = null
|
||||
|
||||
//音频流类型
|
||||
private const val mStreamType = AudioManager.STREAM_MUSIC
|
||||
|
||||
//指定采样率 (MediaRecoder 的采样率通常是8000Hz AAC的通常是44100Hz。 设置采样率为44100,目前为常用的采样率,官方文档表示这个值可以兼容所有的设置)
|
||||
const val mSampleRateIn16KHz: Int = 16000
|
||||
const val mSampleRateIn24KHz: Int = 24000
|
||||
|
||||
//指定捕获音频的声道数目。在AudioFormat类中指定用于此的常量
|
||||
private const val mChannelConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO //单声道
|
||||
|
||||
//指定音频量化位数 ,在AudioFormaat类中指定了以下各种可能的常量。通常我们选择ENCODING_PCM_16BIT和ENCODING_PCM_8BIT PCM代表的是脉冲编码调制,它实际上是原始音频样本。
|
||||
//因此可以设置每个样本的分辨率为16位或者8位,16位将占用更多的空间和处理能力,表示的音频也更加接近真实。
|
||||
private const val mAudioFormat = AudioFormat.ENCODING_PCM_16BIT
|
||||
|
||||
//STREAM的意思是由用户在应用程序通过write方式把数据一次一次得写到audiotrack中。这个和我们在socket中发送数据一样,
|
||||
// 应用层从某个地方获取数据,例如通过编解码得到PCM数据,然后write到audiotrack。
|
||||
private const val mMode = AudioTrack.MODE_STREAM
|
||||
val instance: AudioTrackManager?
|
||||
/**
|
||||
* 获取单例引用
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
get() {
|
||||
if (mInstance == null) {
|
||||
synchronized(AudioTrackManager::class.java) {
|
||||
if (mInstance == null) {
|
||||
mInstance = AudioTrackManager()
|
||||
}
|
||||
}
|
||||
}
|
||||
return mInstance
|
||||
}
|
||||
}
|
||||
|
||||
interface OnCompleteListener {
|
||||
fun onComplete()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package com.mogo.tts.iflytek.offline
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Looper
|
||||
import androidx.annotation.Keep
|
||||
import com.elegant.utils.ResourcesHelper
|
||||
import com.elegant.utils.ThreadPoolService
|
||||
import com.elegant.utils.UiThreadHandler
|
||||
import com.iflytek.aikit.core.AeeEvent
|
||||
import com.iflytek.aikit.core.AiHandle
|
||||
import com.iflytek.aikit.core.AiHelper
|
||||
import com.iflytek.aikit.core.AiInput
|
||||
import com.iflytek.aikit.core.AiListener
|
||||
import com.iflytek.aikit.core.AiRequest
|
||||
import com.iflytek.aikit.core.AiResponse
|
||||
import com.iflytek.aikit.core.AiText
|
||||
import com.iflytek.aikit.core.BaseLibrary
|
||||
import com.iflytek.aikit.core.CoreListener
|
||||
import com.iflytek.aikit.core.ErrType
|
||||
import com.mogo.tts.common.LangTtsEntity
|
||||
import com.mogo.tts.common.log.TtsLogManager
|
||||
import com.mogo.tts.common.impl.BaseMogoTTS
|
||||
import com.mogo.tts.common.utils.FileUtils
|
||||
import java.io.File
|
||||
|
||||
@Keep
|
||||
class IFlyTekOfflineTts : BaseMogoTTS() {
|
||||
|
||||
private var aiHandle: AiHandle? = null
|
||||
private var OUTPUT_DIR :String = ""
|
||||
private val ABILITYID by lazy {
|
||||
"e2e44feff"
|
||||
}
|
||||
|
||||
override fun getTAG(): String {
|
||||
return "IFlyTekOfflineTts"
|
||||
}
|
||||
|
||||
override fun initTts(context: Context) {
|
||||
super.initTts(context)
|
||||
initSDK()
|
||||
}
|
||||
|
||||
private fun initSDK() {
|
||||
context?.let {
|
||||
val workPath = it.filesDir.absolutePath+File.separator+"aikit"+File.separator+"xtts"
|
||||
OUTPUT_DIR = workPath+File.separator+"output"
|
||||
val file = File("$workPath/e3fe94474_1.0.0_xTTS_CnCn_xiaoyan_2018_arm.irf")
|
||||
if (!file.exists() || file.length() == 0L) {
|
||||
ThreadPoolService.execute {
|
||||
FileUtils.copyAssetsToLocal(context, "xTTS", workPath)
|
||||
initEngine(workPath)
|
||||
}
|
||||
} else {
|
||||
ThreadPoolService.execute {
|
||||
initEngine(workPath)
|
||||
}
|
||||
}
|
||||
AiHelper.getInst().registerListener(coreListener)
|
||||
AiHelper.getInst().registerListener(ABILITYID, aiRespListener)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun initEngine(workPath: String) {
|
||||
val params = BaseLibrary.Params.builder()
|
||||
.appId("0c498b42")
|
||||
.apiKey("8579f566eb7f3c4f4a07148ad9e2408c")
|
||||
.apiSecret("NTRmMmI5MWI4NzIzZTIxN2Q5N2FjMWVl")
|
||||
.ability("e2e44feff;e867a88f2")
|
||||
.workDir(workPath)
|
||||
.build()
|
||||
AiHelper.getInst().init(context, params)
|
||||
AudioTrackManager.instance?.setSampleRate(AudioTrackManager.sampleRateType.SAMPLE_RATE_16k)
|
||||
AudioTrackManager.instance?.setListener(completeListener)
|
||||
}
|
||||
|
||||
private val coreListener = CoreListener { type, code ->
|
||||
when (type) {
|
||||
ErrType.AUTH -> {
|
||||
if (code == 0) {
|
||||
// SDK授权成功
|
||||
TtsLogManager.d(tag, "科大讯飞离线语音合成授权成功!")
|
||||
} else {
|
||||
// SDK授权失败,授权码为:code
|
||||
TtsLogManager.d(tag, "科大讯飞离线语音合成授权失败码:$code")
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
// SDK状态为:type, code
|
||||
TtsLogManager.d(tag, "type:$type, code:$code")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val completeListener = object : AudioTrackManager.OnCompleteListener {
|
||||
override fun onComplete() {
|
||||
handleCompleteEvent()
|
||||
}
|
||||
}
|
||||
|
||||
override fun release() {
|
||||
if (Thread.currentThread() != Looper.getMainLooper().thread) {
|
||||
UiThreadHandler.post {
|
||||
AiHelper.getInst().unInit()
|
||||
}
|
||||
} else {
|
||||
AiHelper.getInst().unInit()
|
||||
}
|
||||
}
|
||||
|
||||
override fun realStop() {
|
||||
super.realStop()
|
||||
AudioTrackManager.instance?.stopPlay()
|
||||
if (aiHandle == null || aiHandle?.isSuccess == false) {
|
||||
return
|
||||
}
|
||||
val end = AiHelper.getInst().end(aiHandle)
|
||||
aiHandle = null
|
||||
TtsLogManager.d(tag,"停止tts:${end}")
|
||||
}
|
||||
|
||||
override fun speakMultiLangTTS(ttsEntity: LangTtsEntity) {
|
||||
super.speakMultiLangTTS(ttsEntity)
|
||||
stopTts()
|
||||
startSpeak(ttsEntity)
|
||||
}
|
||||
|
||||
private fun startSpeak(langTtsEntity: LangTtsEntity?) {
|
||||
langTtsEntity?.let {
|
||||
curTtsEntity = it
|
||||
curTtsContent = it.ttsContent
|
||||
realSpeak(it.ttsContent)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 1:中文, 2:英文, 3:法语, 5:日语, 6:俄语, 9:德语, 15:意大利语, 16:韩语, 23:西班牙语, 12:粤语, 8:印地语, 27:泰语
|
||||
*/
|
||||
private fun realSpeak(content: String) {
|
||||
deleteAllOutPutDir()
|
||||
|
||||
// 开启会话
|
||||
val builder = AiInput.builder()
|
||||
builder.param("vcn", "xiaoyan")
|
||||
builder.param("language", 1)
|
||||
builder.param("textEncoding", "UTF-8")
|
||||
builder.param("pitch", 50)
|
||||
builder.param("volume", 50)
|
||||
builder.param("speed", 50)
|
||||
aiHandle = AiHelper.getInst().start(ABILITYID, builder.build(), null)
|
||||
if (!aiHandle!!.isSuccess) {
|
||||
// handleErrorEvent("开启会话报错:${aiHandle!!.code}")
|
||||
val errorInfo = ResourcesHelper.getString(context, R.string.module_tts_start_ai_handle_error)
|
||||
handleErrorEvent("${errorInfo}${aiHandle!!.code}")
|
||||
AiHelper.getInst().end(aiHandle)
|
||||
aiHandle = null
|
||||
return
|
||||
}
|
||||
// 开始写入
|
||||
val dataBuilder = AiRequest.Builder()
|
||||
val input = AiText.get("text").data(content).valid()
|
||||
dataBuilder.payload(input)
|
||||
val ret = AiHelper.getInst().write(dataBuilder.build(), aiHandle)
|
||||
if (ret != 0) {
|
||||
// handleErrorEvent("写能力输入数据失败")
|
||||
val errorInfo = ResourcesHelper.getString(context, R.string.module_tts_write_ai_handle_error)
|
||||
handleErrorEvent(errorInfo)
|
||||
AiHelper.getInst().end(aiHandle)
|
||||
aiHandle = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteAllOutPutDir() {
|
||||
val dir = File(OUTPUT_DIR)
|
||||
if (!dir.exists() || !dir.isDirectory || dir.listFiles() == null) return
|
||||
for (file in dir.listFiles()!!) {
|
||||
if (file.isFile) file.delete() // 删除所有文件
|
||||
}
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
private val aiRespListener = object : AiListener {
|
||||
override fun onResult(handleID: Int, list: MutableList<AiResponse>?, usrCxt: Any?) {
|
||||
if (null != list && list.size > 0) {
|
||||
val dir = File(OUTPUT_DIR)
|
||||
var bytes: ByteArray?
|
||||
for (i in list.indices) {
|
||||
bytes = list[i].value ?: continue
|
||||
TtsLogManager.d(tag, "onResult:handleID:" + handleID + ":" + list[i].key)
|
||||
if (!dir.exists()) {
|
||||
dir.mkdirs()
|
||||
}
|
||||
FileUtils.writeFile("${OUTPUT_DIR}/OutPut_mogo.pcm", bytes)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEvent(
|
||||
handleID: Int,
|
||||
event: Int,
|
||||
eventData: MutableList<AiResponse>?,
|
||||
usrCxt: Any?
|
||||
) {
|
||||
when (event) {
|
||||
AeeEvent.AEE_EVENT_UNKNOWN.value -> {
|
||||
TtsLogManager.d(tag, "未知错误")
|
||||
// handleErrorEvent("未知错误")
|
||||
aiHandle?.let {
|
||||
val ret = AiHelper.getInst().end(it)
|
||||
aiHandle = null
|
||||
TtsLogManager.d(tag, "AIKit_End:$ret")
|
||||
}
|
||||
val errorInfo = ResourcesHelper.getString(context, R.string.module_tts_unknown_error)
|
||||
handleErrorEvent(errorInfo)
|
||||
}
|
||||
AeeEvent.AEE_EVENT_TIMEOUT.value->{
|
||||
TtsLogManager.d(tag, "超时错误")
|
||||
// handleErrorEvent("未知错误")
|
||||
aiHandle?.let {
|
||||
val ret = AiHelper.getInst().end(it)
|
||||
aiHandle = null
|
||||
TtsLogManager.d(tag, "AIKit_End:$ret")
|
||||
}
|
||||
val errorInfo = ResourcesHelper.getString(context, R.string.module_tts_unknown_error)
|
||||
handleErrorEvent(errorInfo)
|
||||
}
|
||||
AeeEvent.AEE_EVENT_START.value->{
|
||||
|
||||
}
|
||||
|
||||
AeeEvent.AEE_EVENT_END.value -> {
|
||||
aiHandle?.let {
|
||||
val ret = AiHelper.getInst().end(it)
|
||||
aiHandle = null
|
||||
TtsLogManager.d(tag, "AIKit_End:$ret")
|
||||
}
|
||||
onSpeakBegin()
|
||||
AudioTrackManager.instance?.startPlay("${OUTPUT_DIR}/OutPut_mogo.pcm")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onError(handleID: Int, err: Int, msg: String?, usrCxt: Any?) {
|
||||
TtsLogManager.d(tag, "错误码:$err,错误信息:$msg")
|
||||
// handleErrorEvent("错误码:$err,错误信息:$msg")
|
||||
val errorInfo = ResourcesHelper.getResources(context)
|
||||
.getString(R.string.module_tts_ai_handle_error_code, err, msg)
|
||||
handleErrorEvent(errorInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="module_tts_start_ai_handle_error">Failed to start session:</string>
|
||||
<string name="module_tts_write_ai_handle_error">Failed to write capability input data</string>
|
||||
<string name="module_tts_ai_handle_error_code">Error code: %d, Error message: %s</string>
|
||||
<string name="module_tts_unknown_error">Unknown error</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="module_tts_start_ai_handle_error">开启会话报错:</string>
|
||||
<string name="module_tts_write_ai_handle_error">写能力输入数据失败</string>
|
||||
<string name="module_tts_ai_handle_error_code">错误码:%d,错误信息:%s</string>
|
||||
<string name="module_tts_unknown_error">未知错误</string>
|
||||
</resources>
|
||||
1
libraries/mogo-tts/tts-mogo/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/build
|
||||
67
libraries/mogo-tts/tts-mogo/README.md
Normal file
@@ -0,0 +1,67 @@
|
||||
### 换肤SDK,
|
||||
#### 使用方式
|
||||
```java
|
||||
public class MoGoApplication extends MultiDexApplication {
|
||||
private static final String TAG = "MoGoApplication";
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
|
||||
// 初始化动态换肤SDK
|
||||
SkinManager.init(this);
|
||||
}
|
||||
}
|
||||
```
|
||||
##### 基础控件0改动完成资源替换,只需要保持使用的「资源文件:drawable、string、color等」皮肤包与APP中保持一致即可
|
||||
TextView 控件的静态文字一定要用string.xml 的方式引用文字显示
|
||||
例如:
|
||||
```xml
|
||||
<TextView
|
||||
android:id="@+id/tvDevicesId"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_margin="5dp"
|
||||
android:text="@string/devicesId"
|
||||
android:textColor="@color/colorAccent"
|
||||
android:textSize="20dp" />
|
||||
```
|
||||
##### 如果代码中动态控制了 ImageView 的 src、background 一定要在 XML 中替换成 SkinImageView.java
|
||||
|
||||
##### 代码中动态获取资源设置:图片、文字、颜色
|
||||
```java
|
||||
// 获取颜色
|
||||
SkinResources.getInstance().getColor(resId);
|
||||
|
||||
// 获取图片
|
||||
SkinResources.getInstance().getDrawable(resId);
|
||||
|
||||
// 获取文字
|
||||
SkinResources.getInstance().getString(resId);
|
||||
|
||||
// 获取指定的资源id
|
||||
SkinResources.getInstance().getIdentifier(resId);
|
||||
|
||||
// 获取指定的Raw资源 InputStream
|
||||
SkinResources.getInstance().getRawInputStream(resId);
|
||||
|
||||
// 获取指定的Raw资源的byte[]
|
||||
SkinResources.getInstance().getRawResourceBytes(resId);
|
||||
|
||||
```
|
||||
|
||||
#### 手动控制切换皮肤
|
||||
```java
|
||||
Skin skin = new Skin(
|
||||
// 皮肤包 MD5 用于文件损坏校验的
|
||||
"d5493244467d3970834e42dc1a6f07c9",
|
||||
// 皮肤文件名称
|
||||
"app-skin-debug.skin",
|
||||
// 外网可访问的文件服务器地址
|
||||
"https://carlife-static-1255510688.cos.ap-beijing.myqcloud.com/MoGoEagleEye/app-skin-debug.skin");
|
||||
|
||||
// 换肤
|
||||
SkinManager.getInstance().selectSkin(this, skin);
|
||||
|
||||
// 还原
|
||||
SkinManager.getInstance().loadSkin("");
|
||||
```
|
||||
44
libraries/mogo-tts/tts-mogo/build.gradle
Normal file
@@ -0,0 +1,44 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'kotlin-android'
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
android {
|
||||
compileSdkVersion rootProject.ext.android.compileSdkVersion
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion rootProject.ext.android.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.android.targetSdkVersion
|
||||
|
||||
versionCode 1
|
||||
versionName "${MOGO_REALTIME_VERSION}"
|
||||
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation rootProject.ext.dependencies.androidxappcompat
|
||||
|
||||
if (Boolean.valueOf(RELEASE)) {
|
||||
implementation "com.mogo.cloud:ttsbase:${TTS_BASE_VERSION}"
|
||||
}else {
|
||||
implementation(project(":libraries:mogo-tts:tts-base"))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
apply from: new File(rootProject.rootDir, "gradle/upload.gradle").toString()
|
||||
0
libraries/mogo-tts/tts-mogo/consumer-rules.pro
Normal file
4
libraries/mogo-tts/tts-mogo/gradle.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
GROUP=com.mogo.cloud
|
||||
POM_ARTIFACT_ID=ttsmogo
|
||||
VERSION_CODE=1
|
||||
VERSION_NAME=1.0.1-SNAPSHOT
|
||||
21
libraries/mogo-tts/tts-mogo/proguard-rules.pro
vendored
Normal file
@@ -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
|
||||
5
libraries/mogo-tts/tts-mogo/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.mogo.tts.offline">
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,34 @@
|
||||
# Introduction
|
||||
|
||||
Model files are from
|
||||
https://modelscope.cn/models/dengcunqin/matcha_tts_zh_en_20251010/summary
|
||||
|
||||
Note that you have to use
|
||||
vocos-16khz-univ.onnx
|
||||
|
||||
You can download it from
|
||||
https://modelscope.cn/models/dengcunqin/matcha_tts_zh_en_20251010/resolve/master/vocos-16khz-univ.onnx
|
||||
or
|
||||
https://github.com/k2-fsa/sherpa-onnx/releases/download/vocoder-models/vocos-16khz-univ.onnx
|
||||
|
||||
```
|
||||
{'am': './model-steps-3.onnx', 'vocoder': './vocos-16khz-univ.onnx', 'tokens': './tokens.txt', 'lexicon': './lexicon.txt', 'text': '中英文合成测试. It supports both English 和中文合成', 'out_wav': 'generated.wav'}
|
||||
|
||||
{'use_eos_bos': '1', 'modelscope_url': 'https://modelscope.cn/models/dengcunqin/matcha_tts_zh_en_20251010', 'sample_rate': '16000', 'language': 'chinese English', 'model_type': 'matcha-tts', 'n_speakers': '1', 'model_author': 'dengcunqin', 'version': '1', 'pad_id': '0', 'voice': 'zh en-us', 'demo_url': 'https://www.tulingyun.com/tts.html', 'num_ode_steps': '3'}
|
||||
|
||||
NodeArg(name='x', type='tensor(int64)', shape=['N', 'L'])
|
||||
NodeArg(name='x_length', type='tensor(int64)', shape=['N'])
|
||||
NodeArg(name='noise_scale', type='tensor(float)', shape=[1])
|
||||
NodeArg(name='length_scale', type='tensor(float)', shape=[1])
|
||||
-----
|
||||
NodeArg(name='mel', type='tensor(float)', shape=['N', 80, 'L'])
|
||||
|
||||
vocos {'modelscope_url': 'https://modelscope.cn/models/dengcunqin/matcha_tts_zh_en_20251010', 'use_eos_bos': '1', 'n_speakers': '1', 'sample_rate': '16000', 'pad_id': '0', 'language': 'chinese English', 'model_type': 'matcha-tts vocos', 'voice': 'zh en-us', 'version': '1', 'demo_url': 'https://www.tulingyun.com/tts.html', 'model_author': 'dengcunqin'}
|
||||
|
||||
----------vocos----------
|
||||
NodeArg(name='mels', type='tensor(float)', shape=['batch_size', 80, 'time'])
|
||||
-----
|
||||
NodeArg(name='mag', type='tensor(float)', shape=['batch_size', 'Clipmag_dim_1', 'time'])
|
||||
NodeArg(name='x', type='tensor(float)', shape=['batch_size', 'Cosx_dim_1', 'time'])
|
||||
NodeArg(name='y', type='tensor(float)', shape=['batch_size', 'Cosx_dim_1', 'time'])
|
||||
```
|
||||