Skip to content

Commit

Permalink
Integrate Google Cloud Translate for simplified Chinese!
Browse files Browse the repository at this point in the history
  • Loading branch information
kevinguitar committed Sep 24, 2023
1 parent f5d611d commit 6ba8704
Show file tree
Hide file tree
Showing 10 changed files with 125 additions and 33 deletions.
38 changes: 37 additions & 1 deletion app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,40 @@
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
-dontwarn org.apiguardian.api.API$Status
-dontwarn org.apiguardian.api.API
-dontwarn org.apiguardian.api.API
-dontwarn com.google.protobuf.AbstractMessage$Builder
-dontwarn com.google.protobuf.AbstractMessage$BuilderParent
-dontwarn com.google.protobuf.AbstractMessage
-dontwarn com.google.protobuf.Descriptors$Descriptor
-dontwarn com.google.protobuf.Descriptors$FieldDescriptor
-dontwarn com.google.protobuf.Descriptors$FileDescriptor
-dontwarn com.google.protobuf.ExtensionRegistry
-dontwarn com.google.protobuf.GeneratedMessage$GeneratedExtension
-dontwarn com.google.protobuf.GeneratedMessage
-dontwarn com.google.protobuf.GeneratedMessageV3$Builder
-dontwarn com.google.protobuf.GeneratedMessageV3$BuilderParent
-dontwarn com.google.protobuf.GeneratedMessageV3$FieldAccessorTable
-dontwarn com.google.protobuf.GeneratedMessageV3
-dontwarn com.google.protobuf.MapEntry
-dontwarn com.google.protobuf.MapField
-dontwarn com.google.protobuf.Message
-dontwarn com.google.protobuf.MessageOrBuilder
-dontwarn com.google.protobuf.ProtocolMessageEnum
-dontwarn com.google.protobuf.RepeatedFieldBuilderV3
-dontwarn com.google.protobuf.SingleFieldBuilderV3
-dontwarn com.google.protobuf.TypeRegistry$Builder
-dontwarn com.google.protobuf.TypeRegistry
-dontwarn com.google.protobuf.UnknownFieldSet
-dontwarn com.google.protobuf.util.Durations
-dontwarn com.google.protobuf.util.JsonFormat$Printer
-dontwarn com.google.protobuf.util.JsonFormat
-dontwarn com.google.protobuf.util.Timestamps
-dontwarn org.eclipse.jetty.alpn.ALPN$ClientProvider
-dontwarn org.eclipse.jetty.alpn.ALPN$Provider
-dontwarn org.eclipse.jetty.alpn.ALPN$ServerProvider
-dontwarn org.eclipse.jetty.alpn.ALPN
-dontwarn org.eclipse.jetty.npn.NextProtoNego$ClientProvider
-dontwarn org.eclipse.jetty.npn.NextProtoNego$Provider
-dontwarn org.eclipse.jetty.npn.NextProtoNego$ServerProvider
-dontwarn org.eclipse.jetty.npn.NextProtoNego
-dontwarn reactor.blockhound.integration.BlockHoundIntegration
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ internal fun Project.configureKotlinAndroid(
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=kotlin.contracts.ExperimentalContracts",
"-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi",
"-opt-in=kotlinx.coroutines.FlowPreview",
)

jvmTarget = Constants.javaVersion.toString()
Expand Down
1 change: 1 addition & 0 deletions core/common/src/main/res/values/constants.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<!-- Google sdk-->
<string name="google_cloud_client_id" translatable="false">919948371655-dpbbs6ip867jd9rcfa1s3p4u9eeu5j20.apps.googleusercontent.com</string>
<string name="google_cloud_translate_api_key" translatable="false">AIzaSyBPAGbdnjJMkA1sZzxdNtFYqcKWNIndezU</string>

<!-- Admob -->
<string name="admob_application_id" translatable="false">ca-app-pub-5636675608309788~9761727103</string>
Expand Down
3 changes: 1 addition & 2 deletions core/common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,8 @@
<!-- Push Notifications -->
<string name="push_notif_title" translatable="false">通知推播</string>
<string name="push_notif_language_zh_tw" translatable="false">繁體中文</string>
<string name="push_notif_language_zh_cn" translatable="false">簡體中文</string>
<string name="push_notif_language_zh_cn" translatable="false">簡體中文(自動翻譯)</string>
<string name="push_notif_language_en" translatable="false">英文</string>
<string name="push_notif_auto_translate_to_cn" translatable="false">自動翻譯至簡體中文(Coming soon!)</string>
<string name="push_notif_push_title" translatable="false">標題</string>
<string name="push_notif_push_description" translatable="false">內容</string>
<string name="push_notif_navigate_to_google_play" translatable="false">跳轉到 Google Play</string>
Expand Down
6 changes: 6 additions & 0 deletions feature/push-notifications/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ plugins {
}

dependencies {
implementation(libs.google.translate) {
// Excluding them for dependencies resolution collision
exclude(group = "com.google.api.grpc")
exclude(group = "com.google.protobuf")
}

implementation(projects.core.data)
implementation(projects.core.ui)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,19 @@ import com.kevlina.budgetplus.core.data.local.PreferenceHolder
import com.kevlina.budgetplus.core.data.remote.PushNotificationData
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Named

@HiltViewModel
internal class PushNotificationsViewModel @Inject constructor(
preferenceHolder: PreferenceHolder,
translator: Translator,
@Named("google_play_url") private val googlePlayUrl: String,
@Named("default_deeplink") private val defaultDeeplink: String,
private val pushDbMediator: PushDbMediator,
Expand All @@ -30,11 +35,6 @@ internal class PushNotificationsViewModel @Inject constructor(
"記得目標,存款不停歇!記帳確實,未來更悠遊!將花費化為理財力!GO~"
)

private var titleCnCache by preferenceHolder.bindString("\uD83C\uDF1E炎夏八月,一起编织理财梦!")
private var descCnCache by preferenceHolder.bindString(
"记得目标,存款不停歇!记帐确实,未来更悠游!将花费化为理财力!GO~"
)

private var titleEnCache by preferenceHolder.bindString("It's a new month!")
private var descEnCache by preferenceHolder.bindString(
"Track your expenses starting from the beginning of the month \uD83D\uDE4C"
Expand All @@ -43,13 +43,19 @@ internal class PushNotificationsViewModel @Inject constructor(
val titleTw = MutableStateFlow(titleTwCache)
val descTw = MutableStateFlow(descTwCache)

val titleCn = MutableStateFlow(titleCnCache)
val descCn = MutableStateFlow(descCnCache)
val titleCn = titleTw
.debounce(INPUT_DEBOUNCE_MS)
.mapLatest { translator.translate(text = it, sourceLanCode = LAN_CODE_TW, targetLanCode = LAN_CODE_CN) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)

val descCn = descTw
.debounce(INPUT_DEBOUNCE_MS)
.mapLatest { translator.translate(text = it, sourceLanCode = LAN_CODE_TW, targetLanCode = LAN_CODE_CN) }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null)

val titleEn = MutableStateFlow(titleEnCache)
val descEn = MutableStateFlow(descEnCache)

val autoTranslateCn = MutableStateFlow(true)
val navigateToGooglePlay = MutableStateFlow(false)
val deeplink = MutableStateFlow("")

Expand All @@ -59,10 +65,10 @@ internal class PushNotificationsViewModel @Inject constructor(

fun sendToEveryone() {
recordToPushDb(isInternal = false)
saveToCache()
}

private fun recordToPushDb(isInternal: Boolean) {
saveToCache()
viewModelScope.launch {
try {
val user = authManager.userState.first()
Expand Down Expand Up @@ -94,10 +100,13 @@ internal class PushNotificationsViewModel @Inject constructor(
titleTwCache = titleTw.value
descTwCache = descTw.value

titleCnCache = titleCn.value
descCnCache = descCn.value

titleEnCache = titleEn.value
descEnCache = descEn.value
}

private companion object {
const val INPUT_DEBOUNCE_MS = 200L
const val LAN_CODE_TW = "zh-TW"
const val LAN_CODE_CN = "zh-CN"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.kevlina.budgetplus.feature.push.notifications

import android.content.Context
import com.google.cloud.translate.Translate.TranslateOption
import com.google.cloud.translate.TranslateOptions
import com.kevlina.budgetplus.core.common.R
import dagger.Reusable
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import timber.log.Timber
import javax.inject.Inject

@Reusable
internal class Translator @Inject constructor(
@ApplicationContext context: Context,
) {
private val translationService by lazy {
@Suppress("DEPRECATION")
TranslateOptions.newBuilder()
.setApiKey(context.getString(R.string.google_cloud_translate_api_key))
.build()
.service
}

suspend fun translate(
text: String,
sourceLanCode: String?,
targetLanCode: String,
): String = withContext(Dispatchers.IO) {
try {
translationService.translate(
text,
TranslateOption.sourceLanguage(sourceLanCode),
TranslateOption.targetLanguage(targetLanCode)
).translatedText
} catch (e: Exception) {
Timber.e(e, "Translation failed, return original text as fallback.")
text
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ internal fun LanguageBlock(
onTitleUpdate: (String) -> Unit,
description: String,
onDescriptionUpdate: (String) -> Unit,
enabled: Boolean = true,
) {

Column(
Expand All @@ -42,13 +43,15 @@ internal fun LanguageBlock(
TextField(
value = title,
onValueChange = onTitleUpdate,
title = stringResource(id = R.string.push_notif_push_title)
title = stringResource(id = R.string.push_notif_push_title),
enabled = enabled
)

TextField(
value = description,
onValueChange = onDescriptionUpdate,
title = stringResource(id = R.string.push_notif_push_description)
title = stringResource(id = R.string.push_notif_push_description),
enabled = enabled
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ internal fun PushNotificationsContent(
val titleEn by vm.titleEn.collectAsStateWithLifecycle()
val descEn by vm.descEn.collectAsStateWithLifecycle()

val autoTranslateCn by vm.autoTranslateCn.collectAsStateWithLifecycle()
val navigateToGooglePlay by vm.navigateToGooglePlay.collectAsStateWithLifecycle()
val deeplink by vm.deeplink.collectAsStateWithLifecycle()

Expand All @@ -67,18 +66,13 @@ internal fun PushNotificationsContent(
onDescriptionUpdate = { vm.descTw.value = it }
)

SwitchBlock(
title = stringResource(id = R.string.push_notif_auto_translate_to_cn),
checked = autoTranslateCn,
onCheckChanged = { vm.autoTranslateCn.value = it }
)

LanguageBlock(
textRes = R.string.push_notif_language_zh_cn,
title = titleCn,
onTitleUpdate = { vm.titleCn.value = it },
description = descCn,
onDescriptionUpdate = { vm.descCn.value = it }
title = titleCn.orEmpty(),
onTitleUpdate = { },
description = descCn.orEmpty(),
onDescriptionUpdate = { },
enabled = false
)

LanguageBlock(
Expand Down
11 changes: 6 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ kotlin = "1.9.10"
# https://central.sonatype.com/artifact/com.google.devtools.ksp/com.google.devtools.ksp.gradle.plugin/versions
ksp = "1.9.10-1.0.13"
# https://developer.android.com/jetpack/compose/bom/bom-mapping
compose-bom = "2023.09.00"
compose-bom = "2023.09.01"
# https://developer.android.com/jetpack/androidx/releases/compose-compiler
compose-compiler = "1.5.3"
# https://firebase.google.com/support/release-notes/android
Expand Down Expand Up @@ -44,12 +44,13 @@ firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "fir
firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics-ktx" }
firebase-firestore = { module = "com.google.firebase:firebase-firestore-ktx" }
firebase-messaging = { module = "com.google.firebase:firebase-messaging-ktx" }
google-ads = "com.google.android.gms:play-services-ads:22.3.0"
google-ads = "com.google.android.gms:play-services-ads:22.4.0"
google-auth = "com.google.android.gms:play-services-auth:20.7.0"
google-billing = "com.android.billingclient:billing-ktx:6.0.1"
google-errorprone = "com.google.errorprone:error_prone_annotations:2.19.1"
google-play-review = "com.google.android.play:review-ktx:2.0.1"
google-play-update = "com.google.android.play:app-update-ktx:2.1.0"
google-translate = "com.google.cloud:google-cloud-translate:2.24.0"
hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" }
hilt-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" }
install-referrer = "com.android.installreferrer:installreferrer:2.2"
Expand All @@ -61,9 +62,9 @@ kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", v
kotlin-immutable-collections = "org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5"
kotlin-serialization = "org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1"
lottie-compose = "com.airbnb.android:lottie-compose:6.1.0"
macro-benchmark = "androidx.benchmark:benchmark-macro-junit4:1.2.0-beta05"
macro-benchmark = "androidx.benchmark:benchmark-macro-junit4:1.2.0-rc01"
mockk = "io.mockk:mockk:1.13.5"
navigation-compose = "androidx.navigation:navigation-compose:2.7.2"
navigation-compose = "androidx.navigation:navigation-compose:2.7.3"
navigation-hilt = "androidx.hilt:hilt-navigation-compose:1.1.0-alpha01"
orchestra-colorpicker = "com.github.skydoves:orchestra-colorpicker:1.2.0"
profile-installer = "androidx.profileinstaller:profileinstaller:1.3.1"
Expand Down Expand Up @@ -104,7 +105,7 @@ android-library = { id = "com.android.library", version.ref = "gradle-plugin" }
android-test = { id = "com.android.test", version.ref = "gradle-plugin" }
detekt = "io.gitlab.arturbosch.detekt:1.23.1"
firebase-crashlytics = "com.google.firebase.crashlytics:2.9.9"
google-services = "com.google.gms.google-services:4.3.15"
google-services = "com.google.gms.google-services:4.4.0"
hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
Expand Down

0 comments on commit 6ba8704

Please sign in to comment.