diff --git a/android/translate-showcase/app/build.gradle b/android/translate-showcase/app/build.gradle index 56cca84013..25bb7e8f92 100644 --- a/android/translate-showcase/app/build.gradle +++ b/android/translate-showcase/app/build.gradle @@ -68,8 +68,8 @@ dependencies { implementation "androidx.camera:camera-view:1.0.0-alpha12" // Add ML Kit dependencies - implementation 'com.google.android.gms:play-services-mlkit-text-recognition:16.0.0' - implementation 'com.google.mlkit:language-id:16.0.0' - implementation 'com.google.mlkit:translate:16.0.0' + implementation 'com.google.android.gms:play-services-mlkit-text-recognition:16.1.0' + implementation 'com.google.mlkit:language-id:16.1.0' + implementation 'com.google.mlkit:translate:16.1.0' } apply plugin: 'com.google.android.gms.strict-version-matcher-plugin' diff --git a/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/analyzer/TextAnalyzer.kt b/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/analyzer/TextAnalyzer.kt index 70916a985f..07a8d6cbf0 100644 --- a/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/analyzer/TextAnalyzer.kt +++ b/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/analyzer/TextAnalyzer.kt @@ -31,7 +31,8 @@ import com.google.mlkit.showcase.translate.util.ImageUtils import com.google.mlkit.vision.common.InputImage import com.google.mlkit.vision.text.Text import com.google.mlkit.vision.text.TextRecognition -import java.lang.Exception +import com.google.mlkit.vision.text.TextRecognizerOptions +import java.util.concurrent.Executor /** * Analyzes the frames passed in from the camera and returns any detected text within the requested @@ -40,10 +41,12 @@ import java.lang.Exception class TextAnalyzer( private val context: Context, lifecycle: Lifecycle, + executor: Executor, private val result: MutableLiveData, private val imageCropPercentages: MutableLiveData> ) : ImageAnalysis.Analyzer { - private val detector = TextRecognition.getClient() + private val detector = + TextRecognition.getClient(TextRecognizerOptions.Builder().setExecutor(executor).build()) init { lifecycle.addObserver(detector) diff --git a/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/main/MainFragment.kt b/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/main/MainFragment.kt index 27d67b9539..e95c1c7eab 100644 --- a/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/main/MainFragment.kt +++ b/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/main/MainFragment.kt @@ -44,7 +44,6 @@ import com.google.mlkit.showcase.translate.analyzer.TextAnalyzer import com.google.mlkit.showcase.translate.util.Language import com.google.mlkit.showcase.translate.util.ScopedExecutor import kotlinx.android.synthetic.main.main_fragment.* -import java.util.concurrent.Executor import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import kotlin.math.abs @@ -108,9 +107,11 @@ class MainFragment : Fragment() { viewFinder = container.findViewById(R.id.viewfinder) // Initialize our background executor - cameraExecutor = Executors.newSingleThreadExecutor() + cameraExecutor = Executors.newCachedThreadPool() scopedExecutor = ScopedExecutor(cameraExecutor) + viewModel.executor = cameraExecutor + // Request camera permissions if (allPermissionsGranted()) { // Wait for the views to be properly laid out @@ -178,8 +179,7 @@ class MainFragment : Fragment() { ) { } - override fun surfaceDestroyed(holder: SurfaceHolder?) { - } + override fun surfaceDestroyed(holder: SurfaceHolder?) {} override fun surfaceCreated(holder: SurfaceHolder?) { holder?.let { @@ -239,6 +239,7 @@ class MainFragment : Fragment() { scopedExecutor, TextAnalyzer( requireContext(), lifecycle, + cameraExecutor, viewModel.sourceText, viewModel.imageCropPercentages ) diff --git a/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/main/MainViewModel.kt b/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/main/MainViewModel.kt index de38cbc588..7f9e1b037c 100644 --- a/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/main/MainViewModel.kt +++ b/android/translate-showcase/app/src/main/java/com/google/mlkit/showcase/translate/main/MainViewModel.kt @@ -23,30 +23,38 @@ import android.util.LruCache import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Transformations import com.google.android.gms.tasks.OnCompleteListener import com.google.android.gms.tasks.Task import com.google.android.gms.tasks.Tasks -import com.google.mlkit.showcase.translate.util.Language -import com.google.mlkit.showcase.translate.util.ResultOrError -import com.google.mlkit.showcase.translate.util.SmoothedMutableLiveData import com.google.mlkit.nl.languageid.LanguageIdentification +import com.google.mlkit.nl.languageid.LanguageIdentificationOptions import com.google.mlkit.nl.translate.TranslateLanguage import com.google.mlkit.nl.translate.Translation import com.google.mlkit.nl.translate.Translator import com.google.mlkit.nl.translate.TranslatorOptions import com.google.mlkit.showcase.translate.main.MainFragment.Companion.DESIRED_HEIGHT_CROP_PERCENT import com.google.mlkit.showcase.translate.main.MainFragment.Companion.DESIRED_WIDTH_CROP_PERCENT +import com.google.mlkit.showcase.translate.util.Language +import com.google.mlkit.showcase.translate.util.ResultOrError +import com.google.mlkit.showcase.translate.util.SmoothedMutableLiveData +import java.util.concurrent.Executor class MainViewModel(application: Application) : AndroidViewModel(application) { - private val languageIdentification = LanguageIdentification.getClient() override fun onCleared() { languageIdentification.close() translators.evictAll() } + lateinit var executor: Executor + private val languageIdentification by lazy { + LanguageIdentification.getClient( + LanguageIdentificationOptions.Builder().setExecutor(executor).build() + ) + } + val targetLang = MutableLiveData() val sourceText = SmoothedMutableLiveData(SMOOTHING_DURATION) + // We set desired crop percentages to avoid having the analyze the whole image from the live // camera feed. However, we are not guaranteed what aspect ratio we will get from the camera, so // we use the first frame we get back from the camera to update these crop percentages based on @@ -75,34 +83,23 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { } } - val sourceLang = Transformations.switchMap(sourceText) { text -> - val result = MutableLiveData() - languageIdentification.identifyLanguage(text) - .addOnSuccessListener { - if (it != "und") - result.value = Language(it) - } - result - } + val sourceLang = MediatorLiveData() private fun translate(): Task { - val text = sourceText.value - val source = sourceLang.value - val target = targetLang.value if (modelDownloading.value != false || translating.value != false) { return Tasks.forCanceled() } - if (source == null || target == null || text == null || text.isEmpty()) { - return Tasks.forResult("") - } - val sourceLangCode = TranslateLanguage.fromLanguageTag(source.code) - val targetLangCode = TranslateLanguage.fromLanguageTag(target.code) - if (sourceLangCode == null || targetLangCode == null) { - return Tasks.forCanceled() - } + val text = sourceText.value ?: return Tasks.forResult("") + val source = sourceLang.value ?: return Tasks.forResult("") + val target = targetLang.value ?: return Tasks.forResult("") + val sourceLangCode = + TranslateLanguage.fromLanguageTag(source.code) ?: return Tasks.forCanceled() + val targetLangCode = + TranslateLanguage.fromLanguageTag(target.code) ?: return Tasks.forCanceled() val options = TranslatorOptions.Builder() .setSourceLanguage(sourceLangCode) .setTargetLanguage(targetLangCode) + .setExecutor(executor) .build() val translator = translators[options] modelDownloading.setValue(true) @@ -127,6 +124,15 @@ class MainViewModel(application: Application) : AndroidViewModel(application) { init { modelDownloading.setValue(false) translating.value = false + + sourceLang.addSource(sourceText) { text -> + languageIdentification.identifyLanguage(text) + .addOnSuccessListener { + if (it != "und") + sourceLang.value = Language(it) + } + } + // Create a translation result or error object. val processTranslation = OnCompleteListener { task -> diff --git a/android/translate-showcase/settings.gradle b/android/translate-showcase/settings.gradle index b4147d2881..eef6b3d65c 100644 --- a/android/translate-showcase/settings.gradle +++ b/android/translate-showcase/settings.gradle @@ -16,4 +16,4 @@ */ include ':app' -rootProject.name='ML Kit Translate Codelab' +rootProject.name='ML Kit Translate Showcase'