After upgrading the lifecycle dependency from 2.6.0-alpha04 to 2.6.0-beta01 I got Unresolved reference: Transformations and it can't import androidx.lifecycle.Transformations class.
import androidx.lifecycle.Transformations
...
var myList: LiveData<List<Bookmark>> = Transformations.switchMap(
bookMarkType
) { input: Int? ->
when (input) {
ARTICLE_BOOKMARK -> return#switchMap repository.articleBookmarks
WEBSITE_BOOKMARK -> return#switchMap repository.websiteBookmarks
LINK_BOOKMARK -> return#switchMap repository.linkBookmarks
}
repository.websiteBookmarks
}
As of 2.6.0-alpha05 version:
Transformations is now written in Kotlin. This is a source incompatible change for those classes written in Kotlin that were directly using syntax such as Transformations.map - Kotlin code must now use the Kotlin extension method syntax that was previously only available when using lifecycle-livedata-ktx. When using the Java programming language, the versions of these methods that take an androidx.arch.core.util.Function method are deprecated and replaced with the versions that take a Kotlin Function1.
So, instead of using Transformations, you need to use the extension function directly myLiveData.switchMap or myLiveData.map
So, to fix this use:
var myList: LiveData<List<Bookmark>> = bookMarkType.switchMap { input: Int? ->
when (input) {
ARTICLE_BOOKMARK -> return#switchMap repository.articleBookmarks
WEBSITE_BOOKMARK -> return#switchMap repository.websiteBookmarks
LINK_BOOKMARK -> return#switchMap repository.linkBookmarks
}
repository.websiteBookmarks
}
Related
I can have
val composeFunction = remember { mutableStateOf ({}) }
I can have
val composeFF = #Composable { Text("ABC") }
val composeFunction = remember { mutableStateOf (composeFF) }
Why can't I have
val composeFunction = remember { mutableStateOf (#Composable { Text("ABC") }) }
It errors out state
Internal Error occurred while analyzing this expression
(Please use the "
" icon in the bottom-right corner to report this error):
jar:file:/Applications/Android%20Studio.app/Contents/lib/platform-impl.jar!/general/error.svg
Type inference failed. Expected type mismatch: inferred type is #Composable () -> Unit but () -> Unit was expected
Have you tried specifying the type?
val composeFunction = remember { mutableStateOf<#Composable () -> Unit> (#Composable { Text("ABC") }) }
Looks like the compiler cannot infer an ordinary function to something that is supplied with a #Composable annotation
Update:
It turns out #Composable annotation actually modifies the function type, similar to what suspend modifier does.
Based on this arcticle,
An important thing to note is that Compose is not an annotation
processor. Compose works with the aid of a Kotlin compiler plugin in
the type checking and code generation phases of Kotlin: there is no
annotation processor needed in order to use compose. This annotation
more closely resembles a language keyword. A good analogy is Kotlin’s
suspend keyword.
furthermore,
The important point here is that when you annotate a function type
with #Composable you’re changing its type: the same function type
without the annotation is not compatible with the annotated type.
Also, suspend functions require a calling context, meaning that you
can only call suspend functions inside of another suspend function.
I have the below working code which uses a dropdown to update the satusFilterFlow to allow for the filtering of characters through the getCharacterList call. The getCharacterList call uses the jetpack paging and returns Flow<PagerData<Character>>.
private val statusFilterFlow = MutableStateFlow<StatusFilter>(NoStatusFilter)
// private val searchFilterFlow = MutableStateFlow<SearchFilter>(NoSearchFilter)
val listData: LiveData<PagingData<Character>> =
statusFilterFlow.flatMapLatest{ statusFilter ->
characterRepository.getCharacterList(null, statusFilter.status)
.cachedIn(viewModelScope)
.flowOn(Dispatchers.IO)
}.asLiveData()
Given the above working solution, what is the correct flow extension to allow for me to add multiple StateFlows as I build out additional filters (e.g. SearchFilter).
I have tried combineTransorm as follows:
private val statusFilterFlow = MutableStateFlow<StatusFilter>(NoStatusFilter)
private val searchFilterFlow = MutableStateFlow<SearchFilter>(NoSearchFilter)
val listData: LiveData<PagingData<Character>> =
statusFilterFlow.combineTransform(searchFilterFlow) { statusFilter, searchFilter ->
characterRepository.getCharacterList(searchFilter.search, statusFilter.status)
.flowOn(Dispatchers.IO)
.cachedIn(viewModelScope)
}.asLiveData()
However, this gives me a "Not enough information to infer type variable R" error.
The usual way to understand and/or fix those errors is to specify types explicitly in the function call:
statusFilterFlow.combineTransform<StatusFilter, SearchFilter, PagingData<Character>>(searchFilterFlow) { ... }
This is orthogonal to the problem at hand, but I'd also suggest using the top-level combineTransform overload that takes all source flows as argument (instead of having the first one as receiver), so there is a better symmetry. Since I believe there is no reason one of the filters is more special than the other.
All in all, this gives:
val listData: LiveData<PagingData<Character>> =
combineTransform<StatusFilter, SearchFilter, PagingData<Character>>(statusFilterFlow, searchFilterFlow) { statusFilter, searchFilter ->
characterRepository.getCharacterList(searchFilter.search, statusFilter.status)
.flowOn(Dispatchers.IO)
.cachedIn(viewModelScope)
}.asLiveData()
For anymore else, this is too complex or doesn't work out for you ... Use Combine then flatMap latest on the top of that.
private val _selectionLocation: MutableStateFlow<Location?> = MutableStateFlow(null)
val searchKeyword: MutableStateFlow<String> = MutableStateFlow("")
val unassignedJobs: LiveData<List<Job>> =
combine(_selectionLocation, searchKeyword) { location: Location?, keyword: String ->
Log.e("HomeViewModel", "$location -- $keyword")
location to keyword
}.flatMapLatest { pair ->
_repo.getJob(Status.UNASSIGNED, pair.first).map {
Log.e("HomeViewModel", "size ${it.size}")
it.filter { it.desc.contains(pair.second) }
}
}.flowOn(Dispatchers.IO).asLiveData(Dispatchers.Main)
I am trying to learn kotlin from basic android kotlin codelabs here, where a codelabs explains lambda and higher order functions. It shows an example of higher order function
sortedWith()
we are using this method if we have to sort names list based on string length, as given in the codelab
fun main() {
val peopleNames = listOf("Fred", "Ann", "Barbara", "Joe")
println(peopleNames.sorted())
println(peopleNames.sortedWith { str1: String, str2: String -> str1.length - str2.length })
}
The output of the above is given :
[Ann, Barbara, Fred, Joe]
[Ann, Joe, Fred, Barbara]
which is working fine, if i work on kotlin playground : here
However, if I try to run this code on IntelliJ IDEA, I am getting an error :
Error:(37, 25) Kotlin: Type inference failed: fun <T> Iterable<T>.sortedWith(comparator: kotlin.Comparator<in T> /* = java.util.Comparator<in T> */): List<T>
cannot be applied to
receiver: List<String> arguments: ((String, String) -> Int)
Error:(37, 35) Kotlin: Type mismatch: inferred type is (String, String) -> Int but kotlin.Comparator<in String> /* = java.util.Comparator<in String> */ was expected
Is there anything wrong with my kotlin version? My current kotlin version is :
1.3.50-release-112
Use compareBy
println( peopleNames.sortedWith(compareBy(
{ it.length },
{ it }
)))
output
[Ann, Barbara, Fred, Joe]
[Ann, Joe, Fred, Barbara]
When building a AbstractProcessor in Android studio using kapt/kotlinpoet. When I try to use the repeatable annotation tag it stop getting data back from roundEnv.getElementsAnnotatedWith(AnnotationName::class.java), I am able get the annotated classes info back if the repeatable tag is removed from the annotation
going to try to use other means of reflection
#Target(AnnotationTarget.CLASS)
#Retention(AnnotationRetention.SOURCE)
#Repeatable // <-- issue
annotation class ConfigurableGroup(
val key: String,
val text: String
)
// the processor abbreviated
#AutoService(Processor::class)
#SupportedSourceVersion(SourceVersion.RELEASE_8)
#SupportedOptions(AnnotationProcessorNew.
KAPT_KOTLIN_GENERATED_OPTION_NAME)
class AnnotationProcessorNew : AbstractProcessor(){
override fun process(annotations: MutableSet<out TypeElement>, roundEnv:
RoundEnvironment): Boolean {
return generateConfigurables(roundEnv)
}
override fun getSupportedAnnotationTypes(): MutableSet<String> {
return mutableSetOf(
ConfigurableGroup::class.java.name
}
roundEnv.getElementsAnnotatedWith(ConfigurableGroup::class.java)
.filter { it.kind == ElementKind.CLASS }
.forEach { classElement ->
val classPackageName =
processingEnv.elementUtils.getPackageOf(classElement).toString()
val classSimpleName = classElement.simpleName.toString()
I would expect to get data from reflection both times when the annotation has a #repeatable tag or not.
It seems that Kotlin's #Repeatable annotation is purely a hint for tooling and does not match up with Java's #Repeatable contract.
It seems that the java contract requires a container annotation to be defined so that the repeated annotations can be packaged as a single annotation for retrieval by the annotation processor.
You either need to explicitly add #java.lang.annotation.Repeatable(ConfigurableGroups::class) for a container annotation and accept the warning it generates or define the annotations in Java instead of Kotlin.
I am trying to test this on Kotlin:
verify(myInterface).doSomething(argumentCaptor.capture())
capture.value.invoke(0L)
Where doSomething is:
doSomething((Long) -> Unit)
How can I create an ArgumentCaptor for this? Right now I am doing this
inline fun <reified T : Any> argumentCaptor() = ArgumentCaptor.forClass(T::class.java)!!
val captor = argumentCaptor<(Long) -> Unit>()
verify(mainApiInterface!!).downloadUserProfilePicture(captor.capture())
captor.value.invoke(0L)
But I am getting java.lang.IllegalStateException: captor.capture() must not be null
I also tried integrating mockito-kotlin but I get a PowerMockito error:
No instance field named "reported" could be found in the class hierarchy of org.mockito.internal.MockitoCore.
Using mockito-kotlin like this seems to work:
val myService = mock<MyInterface>()
myService.doSomething {
println(it)
}
verify(myService).doSomething(capture { function ->
function.invoke(123)
})
Edit: removed unnecessary argumentCaptor<(Long) -> Unit>().apply {} - it wasn't used
as with kotlin 1.3.72 and com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0 the following works fine for me:
create an argument captor via val captor = argumentCaptor<() -> Unit>() and call captor.capture() on it.
There's also a variant for nullable captors with nullableArgumentCaptor()
The following unit-test captures the lambda of type () -> Unit that is given to diff.open(). To capture it at runtime it then uses captor.capture()
// given
val onClose = argumentCaptor<() -> Unit>()
// when
diff.open(file, serialized) { onDiffClosed(clusterResource, documentBeforeDiff) }
// then
verify(diff).open(any(), any(), onClose.capture())
The nhaarman wrapper for mockito creates a wrapper KArgumentCaptor for the mockito class ArgumentCaptor. The nhaarman wrapper fixes your error by creating an instance instead of null as in mockito.