I'm using Firebase ML Kit on my app to translate strings. But since I updated my gradle dependencies, some classes can't be found and are marked as unresolved on Android Studio.
This is my build.gradle file:
implementation 'com.google.firebase:firebase-ml-natural-language:22.0.0'
implementation 'com.google.firebase:firebase-ml-natural-language-translate-model:20.0.7'
This is my code:
private val modelManager: FirebaseTranslateModelManager =
FirebaseTranslateModelManager.getInstance()
// ...
// function to download the models
fun downloadLanguage(language: Language) {
val model = getModel(FirebaseTranslateLanguage.languageForLanguageCode(language.code))
modelManager.downloadRemoteModelIfNeeded(model)
.addOnCompleteListener { fetchDownloadedModels() }
}
// ...
// function to load the downloaded models
private fun fetchDownloadedModels() {
modelManager.getAvailableModels(FirebaseApp.getInstance())
.addOnSuccessListener { remoteModels ->
availableModels.value =
remoteModels.sortedBy { it.languageCode }.map { it.languageCode }
}
}
Android Studio tells me FirebaseTranslateModelManager is unresolved. What should I do?
That's because the latest version of ML Kit Translate Text (22.0.0) introduced some breaking changes, as mentioned on the release notes:
Breaking change: Updated FirebaseTranslateRemoteModel with the following changes to simplify the developer workflow:
Removed the setFirebaseApp method in Builder. A custom FirebaseApp is now supported through the getInstance method in FirebaseModelManager.
Removed the setDownloadConditions method in Builder. Download conditions are now passed to the download method in FirebaseModelManager.
Breaking change: Removed FirebaseTranslateModelManager. Translate models are now handled through FirebaseModelManager.
So you'll need to update your code to:
private val modelManager: FirebaseModelManager = FirebaseModelManager.getInstance()
// ...
fun downloadLanguage(language: Language) {
val model = getModel(FirebaseTranslateLanguage.languageForLanguageCode(language.code)!!)
val conditions = FirebaseModelDownloadConditions.Builder()
.requireWifi()
.build()
modelManager.download(model, conditions)
.addOnCompleteListener { fetchDownloadedModels() }
}
// ...
private fun fetchDownloadedModels() {
modelManager.getDownloadedModels(FirebaseTranslateRemoteModel::class.java)
.addOnSuccessListener { remoteModels ->
availableModels.value =
remoteModels.sortedBy { it.languageCode }.map { it.languageCode }
}
}
Related
I'm trying to migrate from version 3.0.0 that used conductor-rxlifecycle to version 3.1.4 that is using conductor-archlifecycle and conductor-autodispose.
my current code has extension functions that binds to the lifecycle - and I'm trying to understand what is the code change needed to adjust it to archlifecycle and auto-dispose.
I would appreciate some help here - couldn't figure it out from the demo code.
conductor-archlifecycle demo
conductor-autodispose demo
protected fun <C : RxController> Completable.bindToController(controller: C): Completable =
observeOn(AndroidSchedulers.mainThread()).compose(controller.bindToLifecycle<Any>())
protected fun <C : RxController> Completable.bindUntil(controller: C, event: ControllerEvent): Completable =
observeOn(AndroidSchedulers.mainThread()).compose(controller.bindUntilEvent<Any>(event))
I assume that the controller type should be LifecycleController instead of RxController, but I don't understand what is the replacement of bindToLifecycle
I opened this issue , but I'm trying to get some help here as well
This is the change I did to my code to match the new Conductor version:
The 2 functions above were replaced by this function:
fun Completable.autoDisposable(event: ControllerEvent? = null): CompletableSubscribeProxy =
observeOn(AndroidSchedulers.mainThread())
.autoDisposable(getScopeProvider(event))
Note that the return type is now CompletableSubscribeProxy and not Completable so the location of the call in the chain might need to be changed.
I create different scopes:
private val scopeProvider: ControllerScopeProvider by lazy { ControllerScopeProvider.from(this) }
private val destroyScopeProvider: ControllerScopeProvider by lazy {
ControllerScopeProvider.from(
this,
ControllerEvent.DESTROY
)
}
...
And this is how getScopeProvider looks
private fun getScopeProvider(event: ControllerEvent?): ControllerScopeProvider =
when (event) {
null -> scopeProvider
ControllerEvent.DETACH -> detachScopeProvider
ControllerEvent.DESTROY_VIEW -> destroyViewScopeProvider
ControllerEvent.DESTROY -> destroyScopeProvider
else -> throw RuntimeException("Scope for event ${event.name} wasn't created")
}
I am trying to run a Google ML Kit function and the result will be in callback and need to pass that value as a return type for the method in which it was executing in Kotlin. I tried some of the samples of Kotlin coroutines but still I am missing something and it was failing. I am still learning Kotlin.
internal fun processImageSync(image: InputImage) : String{
var doctype = ""
val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
recognizer.process(image)
.addOnSuccessListener { visionText ->
var texttoscan = visionText.text.trim()
doctype = findstr(texttoscan)
}
.addOnFailureListener {
}
return doctype;
}
How can I solve the issue?
Use kotlinx-coroutines-play-services module
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.6.0")
}
Use extension Task.await
internal suspend fun processImageSync(image: InputImage): String {
val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)
return recognizer.process(image)
.await()
.let { visionText -> findstr(visionText.text.trim()) }
}
i'm studying android by a book. according to the book, i need to create a repository to get data from the network response. and i had create a livedata to store info.
here are the codes on my book.
object Repository {
fun searchPlaces(query: String) = liveData(Dispatchers.IO) {
val result = try {
val placeResponse = SunnyWeatherNetwork.searchPlaces(query)
if (placeResponse.status == "ok") {
val places = placeResponse.places
Result.success(places)
} else {
Result.failure(RuntimeException("response status is ${placeResponse.status}"))
}
} catch (e: Exception) {
Result.failure<Place>(e)
}
emit(result)
}
But when I copied the codes, i found that the second row's Dispatchers.IO was wrong. and i tried to import the kotlinx.coroutines.Dispatchers, the IDE can't find it.
finally i abandoned the codes, and the app run successfully. i can't understand the specific principles
this is my build.gradle.
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
and how can i use the Dispatchers.IO?
Dispatchers.IO belongs to kotlinx-coroutines-core package.
Add kotlinx-coroutines-core implementation to your module's build.gradle
dependencies{
def coroutinesVersion = '1.5.2-native-mt'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
}
Overview
Expected Behavior
Replace mock object initialization using the mockObject function implementation with annotation syntax initialization for JUnit 5 as outlined in the documentation and Medium post by #oleksiyp.
Current Behavior
The test in question is a parameterized test as outlined by #phauer for JUnit 5 which seems to conflict with #ExtendWith(MockKExtension::class). In order to implement tests with LiveData the test must run synchronously in the local unit test using this InstantExecutorExtension designed by #JeroenMols.
Mock object initialization works as expected with the mockObject function, but fails using the annotation #MockK.
Error
Warning message/Build fail:
Repeatable annotations with non-SOURCE retention are not yet supported.
Implementation
mockObject function implementation (Working as expected)
#ExtendWith(InstantExecutorExtension::class)
class NavigateContentTests {
private val mainThreadSurrogate = newSingleThreadContext("UI thread")
private val contentViewModel = ContentViewModel()
// This is the stream of tests to run in the Parameterized test below.
private fun NavigateContent() = Stream.of(
NavigateContentTest(
isRealtime = false,
feedType = MAIN,
timeframe = DAY,
mockFeedList = mockDbContentListForDay,
mockContent = mockArticleContent),
...)
#BeforeAll
fun beforeAll() { mockkObject(ContentRepository) }
#AfterAll
fun afterAll() { unmockkAll() // Re-assigns transformation of object to original state prior to mock. }
#BeforeEach
fun beforeEach() { Dispatchers.setMain(mainThreadSurrogate) }
#AfterEach
fun afterEach() {
Dispatchers.resetMain() // Reset main dispatcher to the original Main dispatcher.
mainThreadSurrogate.close()
}
#ParameterizedTest
#MethodSource("NavigateContent")
fun `Navigate Content`(test: NavigateContentTest) = runBlocking {
every { ContentRepository.getMainFeedList(test.isRealtime, any()) } returns mockGetMainFeedList(
test.mockFeedList, CONTENT)
every {
ContentRepository.queryLabeledContentList(test.feedType)
} returns mockQueryMainContentList(test.mockFeedList)
every { ContentRepository.getContent(test.mockContent.id) } returns mockGetContent(test)
// Tests here...
// Verification here...
}
}
Annotation syntax initialization (Not working due to two extensions #ExtendWith)
#ExtendWith(InstantExecutorExtension::class)
#ExtendWith(MockKExtension::class)
class NavigateContentTests {
// This object should be mocked.
#MockK
lateinit var contentRepository: ContentRepository
private val mainThreadSurrogate = newSingleThreadContext("UI thread")
private val contentViewModel = ContentViewModel()
// This is the stream of tests to run in the Parameterized test below.
private fun NavigateContent() = Stream.of(
NavigateContentTest(
isRealtime = false,
feedType = MAIN,
timeframe = DAY,
mockFeedList = mockDbContentListForDay,
mockContent = mockArticleContent),
...)
#BeforeAll
fun beforeAll() { MockKAnnotations.init(this, relaxUnitFun = true) // turn relaxUnitFun on for }
#AfterAll
fun afterAll() { unmockkAll() // Re-assigns transformation of object to original state prior to mock. }
#BeforeEach
fun beforeEach() { Dispatchers.setMain(mainThreadSurrogate) }
#AfterEach
fun afterEach() {
Dispatchers.resetMain() // Reset main dispatcher to the original Main dispatcher.
mainThreadSurrogate.close()
}
#ParameterizedTest
#MethodSource("NavigateContent")
fun `Navigate Content`(test: NavigateContentTest) = runBlocking {
every { contentRepository.getMainFeedList(test.isRealtime, any()) } returns mockGetMainFeedList(
test.mockFeedList, CONTENT)
every {
contentRepository.queryLabeledContentList(test.feedType)
} returns mockQueryMainContentList(test.mockFeedList)
every { contentRepository.getContent(test.mockContent.id) } returns mockGetContent(test)
// Tests here...
// Verification here...
}
}
Environment
MockK version: 1.9.3
OS: Mac 10.14.6
Kotlin version: 1.3.50
JDK version: 12.0.1
JUnit version: 5.5.1
Type of test: Unit test
This is a bug according to this GitHub issue, as documented by the MockK creator, #oleksiy.
I will update this post once I see the bug is resolved.
I am using the Play Services Auth api Phone and so far I have the foll
fun startSmsListener() {
val client = SmsRetriever.getClient(applicationContext /* context */);
val task = client.startSmsRetriever();
task.addOnSuccessListener(object : OnSuccessListener<Void> {
override fun onSuccess(p0: Void?) {
//do somethin
}
})
task.addOnFailureListener(object : OnFailureListener {
override fun onFailure(p0: Exception) {
//Handle error
}
})
}
Now I want to put this in an SmsManager class and convert it into an Single/Observable so I can handle it in a reactive way in my viewmodel. How can I do that?
So far I've got this:
var single = Single.create(SingleOnSubscribe<Void> { e ->
val task = client.startSmsRetriever()
task.addOnSuccessListener {
e.onSuccess(it)
}
task.addOnFailureListener {
e.onError(it)
}
})
But I am unsure as to whether this code is correct or not, whether there is something im missing like removing the listeners after disposal.
Any help?
You are interested in a "boolean" value - either connected or not connected, thus instead of Single you should use Completable:
Completable.create { emitter ->
val client = SmsRetriever.getClient(applicationContext)
val task = client.startSmsRetriever()
task.addOnSuccessListener { emitter.onComplete() }
task.addOnFailureListener { emitter.tryOnError(it) }
}
While creating a Completable manually will work, you might also have a look at the RxTask project. It provides "RxJava 2 binding for Google Play Services Task APIs".
If you need it just in one place, an extra library would certainly be an overkill. But if you plan to use more Play Services together with RxJava, it might be worth a look...
It doesn't (yet) provide a wrapper explicitly for SmsRetriever, but the general task helper classes would probably be enough:
val client = SmsRetriever.getClient(applicationContext)
val smsReceiver = CompletableTask.create(client::startSmsRetriever)