Adding a custom class breaks Compose compilation - android

I'm just trying to get started with Android development and Kotlin using Jetpack Compose. Note that, I'm a Kotlin novice, so I'm trying to learn along the way. I come from JavaScript/TypeScript background, so I'm trying to learn by thinking in JavaScript terms and implement in Kotlin terms by searching online.
I'm trying to list all installed applications on the device. While the app was working as expected up till now, I needed to add a feature to sort the installed app names. I referred to: https://www.bezkoder.com/kotlin-sort-list-objects/#Create_Class_for_handling_sorting. As soon as I added a custom class to sort the List<ApplicationInfo>, my app stopped building.
I have included my repo here: https://github.com/Hrishikesh-K/TryKotlin
If I comment these lines and this line as well, the app builds fine. With the current setup, I get an error:
Functions which invoke #Composable functions must be marked with the #Composable annotation
which points to line 21, character 18, which is the start of the word compare.
I don't understand why Compose would care about a custom class, it's not a Composable function after all. What am I missing?

In the compare method you are using LocalContext.current
override fun compare(o1 : ApplicationInfo, o2 : ApplicationInfo): Int {
return o1.loadLabel(LocalContext.current.packageManager).toString().compareTo(o2.loadLabel(LocalContext.current.packageManager).toString())
}
You can't use a #Composable functions if the method is not marked with the #Composable.
Use something different like:
data class CompareApplicationNames(val context: Context) : Comparator<ApplicationInfo> {
override fun compare(o1 : ApplicationInfo, o2 : ApplicationInfo): Int {
return o1.loadLabel(context.packageManager).toString().compareTo(o2.loadLabel(context.packageManager).toString())
}
}
Then just use:
Log.d("sorted:", listOfApplications.sortedWith(CompareApplicationNames(LocalContext.current)).toString())

Related

Is it possible to load a native library in a Jetpack Compose #preview function?

I've begun implementing a feature on my application with Jetpack Compose.
The application uses Swig to generate code that allows me to interact with a C++ layer from Kotlin/Java. In order to use the C++ layer, I load the library like this in the app's main activity:
static {
System.loadLibrary("{swig library name}");
}
When I attempt to use Jetpack Compose previews, I get the following render problem:
java.lang.UnsatisfiedLinkError: 'void {package containing swig's generated code}.swigWrappersJNI.swig_module_init()'
The function does use a class generated by Swig, so I'm assuming that's why this is happening.
I tried getting around this by adding the following to the preview function:
System.loadLibrary("{swig library name}")
but that just results in the following render problem:
java.lang.UnsatisfiedLinkError: no {swig library name} in java.library.path:
Does anybody know how/if I can properly link the library so that I can use Compose's preview feature as I work on this app?
Edit:
For a little more info, I've tried the following methods for loading my library:
#Composable
#Preview
fun ___Preview() {
System.loadLibrary("{swig library name}");
// Actual preview code here
}
object LibraryLoader {
fun load() {
System.loadLibrary("{swig library name}")
}
}
#Composable
#Preview
fun ___Preview() {
LibraryLoader.load()
// Actual preview code here
}
(This one was a bit of a shot in the dark. I thought the key difference could've been loading it within a static block, but this had no effect.)
The previews can be successfully deployed to a device/emulator.
I think the issue here is that the library lives in lib/{abi}/{swig library name}.so, so it exists when running on a device/emulator (with a supported ABI) but not for Compose previews.
What I've done to work around it is create a copy of my model classes that don't use any of Swig's generated code so I can take advantage of previews when creating the layout, then replacing them with the actual model classes for testing functionality. Definitely not a solution I'm particularly happy with but it may be the only way to use Compose previews in this type of situation.

Where to do Arrow.io IO.runUnsafeSync() ? ViewModel or Activity/Fragment?

I'm trying to learn the Arrow library and improve my functional programming by transitioning some of my Android Kotlin code from more imperative style to functional style. I've been doing a type of MVI programming in the application to make testing simpler.
"Traditional" Method
ViewModel
My view model has a LiveData of the view's state plus a public method to pass user interactions from the view to the viewmodel so the view model can update state in whatever way is appropriate.
class MyViewModel: ViewModel() {
val state = MutableLiveData(MyViewState()) // MyViewState is a data class with relevant data
fun instruct(intent: MyIntent) { // MyIntent is a sealed class of data classes representing user interactions
return when(intent) {
is FirstIntent -> return viewModelScope.launch(Dispatchers.IO) {
val result = myRoomRepository.suspendFunctionManipulatingDatabase(intent.myVal)
updateStateWithResult(result)
}.run { Unit }
is SecondIntent -> return updateStateWithResult(intent.myVal)
}
}
}
Activity
The Activity subscribes to the LiveData and, on changes to state, it runs a render function using the state. The activity also passes user interactions to the view model as intents (not to be confused with Android's Intent class).
class MyActivity: AppCompatActivity() {
private val viewModel = MyViewModel()
override fun onCreateView() {
viewModel.state.observe(this, Observer { render(it) })
myWidget.onClickObserver = {
viewModel.instruct(someIntent)
}
}
private fun render(state: MyViewState) { /* update view with state */ }
}
Arrow.IO Functional Programming
I'm having trouble finding examples that aren't way over my head using Arrow's IO monad to make impure functions with side effects obvious and unit-testable.
View Model
So far I have turned my view model into:
class MyViewModel: ViewModel() {
// ...
fun instruct(intent: MyIntent): IO<Unit> {
return when(intent) {
is FirstIntent -> IO.fx {
val (result) = effect { myRoomRepository.suspendFunctionManipulatingDatabase(intent.myVal) }
updateStateWithResult(result)
}
is SecondIntent -> IO { updateStateWithResult(intent.myVal) }
}
}
}
I do not know how I am supposed to make this IO stuff run in Dispatcher.IO like I've been doing with viewModelScope.launch. I can't find an example for how to do this with Arrow. The ones that make API calls all seem to be something other than Android apps, so there is no guidance about Android UI vs IO threads.
View model unit test
Now, because one benefit I'm seeing to this is that when I write my view model's unit tests, I can have a test. If I mock the repository in order to check whether suspendFunctionManipulatingDatabase is called with the expected parameter.
#Test
fun myTest() {
val result: IO<Unit> = viewModel.instruct(someIntent)
result.unsafeRunSync()
// verify suspendFunctionManipulatingDatabase argument was as expected
}
Activity
I do not know how to incorporate the above into my Activity.
class MyActivity: AppCompatActivity() {
private val viewModel = MyViewModel()
override fun onCreateView() {
viewModel.state.observe(this, Observer { render(it) })
myWidget.onClickObserver = {
viewModel.instruct(someIntent).unsafeRunSync() // Is this how I should do it?
}
}
// ...
}
My understanding is anything in an IO block does not run right away (i.e., it's lazy). You have to call attempt() or unsafeRunSync() to get the contents to be evaluated.
Calling viewModel.instruct from Activity means I need to create some scope and invoke in Dispatchers.IO right? Is this Bad(TM)? I was able to confine coroutines completely to the view model using the "traditional" method.
Where do I incorporate Dispatchers.IO to replicate what I did with viewModelScope.launch(Dispatchers.IO)?
Is this the way you're supposed to structure a unit test when using Arrow's IO?
That's a really good post to read indeed. I'd also recommend digging into this sample app I wrote that is using ArrowFx also.
https://github.com/JorgeCastilloPrz/ArrowAndroidSamples
Note how we build the complete program using fx and returning Kind at all levels in our architecture. That makes the code polymorphic to the type F, so you can run it using different runtime data types for F at will, depending on the environment. In this case we end up running it using IO at the edges. That's the activity in this case, but could also be the application class or a fragment. Think about this as what'd be the entry points to your apps. If we were talking about jvm programs the equivalent would be main(). This is just an example of how to write polymorphic programs, but you could use IO.fx instead and return IO everywhere, if you want to stay simpler.
Note how we use continueOn() in the data source inside the fx block to leave and come back to the main thread. Coroutine context changes are explicit in ArrowFx, so the computation jumps to the passed thread right after the continueOn until you deliberately switch again to a different one. That intentionally makes thread changes explicit.
You could inject those dispatchers to use different ones in tests. Hopefully I can provide examples of this soon in the repo, but you can probably imagine how this would look.
For the syntax on how to write tests note that your program will return Kind (if you go polymorphic) or IO, so you would unsafeRunSync it from tests (vs unsafeRunAsync or unsafeRunAsyncCancellable in production code since Android needs it to be asynchronous). That is because we want our test to be synchronous and also blocking (for the latter we need to inject the proper dispatchers).
Current caveats: The solution proposed in the repo still doesn't care of cancellation, lifecycle or surviving config changes. That's something I'd like to address soon. Using ViewModels with a hybrid style might have a chance. This is Android so I'd not fear hybrid styles if that brings better productivity. Another alternative I've got in mind would maybe be something a bit more functional. ViewModels end up retaining themselves using the retain config state existing APIs under the hood by using the ViewModelStore. That ultimately sounds like a simple cache that is definitely a side effect and could be implemented wrapped into IO. I want to give a thought to this.
I would definitely also recommend reading the complete ArrowFx docs for better understanding: https://arrow-kt.io/docs/fx/ I think it would be helpful.
For more thoughts on approaches using Functional Programming and Arrow to Android you can take a look to my blog https://jorgecastillo.dev/ my plan is to write deep content around this starting 2020, since there's a lot of people interested.
In the other hand, you can find me or any other Arrow team maintainers in the Kotlinlang JetBrains Slack, where we could have more detailed conversations or try to resolve any doubts you can have https://kotlinlang.slack.com/
As a final clarification: Functional Programming is just a paradigm that resolves generic concerns like asynchrony, threading, concurrency, dependency injection, error handling, etc. Those problems can be found on any program, regardless of the platform. Even within an Android app. That is why FP is an option as valid for mobile as any other one, but we are still into explorations to provide the best APIs to fulfill the usual Android needs in a more ergonomic way. We are in the process of exploration in this sense, and 2020 is going to be a very promising year.
Hopefully this helped! Your thoughts seem to be well aligned with how things should work in this approach overall.

How is the Resource class correctly overwritten to implement a dynamic translation system?

Hello to all Android Developers, I need to clarify a doubt in relation to the management of dynamic resources in Android applications.
I need my application to use the translations returned by my backend depending on the language configured on the phone.
I wanted to implement it in an elegant way working on a custom LayoutInflater that applies a ViewTransformer depending on the type of graphic component.
Each ViewTransformer will only collect the identifier (for example #id/landing_welcome_text) and make the next call:
val value = attrs.getAttributeValue(index)
if (value != null && value.startsWith("#")) {
val text = view.context.resources.getString(attrs.getAttributeResourceValue(index, 0))
setTextForView(view, text)
}
A ContextWrapper has been implemented that returns my custom LayoutInflater and a Resource implementation
override fun getSystemService(name: String): Any {
return if (Context.LAYOUT_INFLATER_SERVICE == name)
CustomLayoutInflater(
LayoutInflater.from(baseContext),
this,
viewTransformerManager
)
else
super.getSystemService(name)
}
override fun getResources(): Resources = customResources
The problem is that overwriting the behavior of the Resources class is considered a deprecated strategy.
As the documentation says:
This constructor is deprecated. Resources should not be constructed by
apps. See Context.createConfigurationContext(Configuration).
class CustomResourcesWrapper constructor(
res: Resources,
private val languageStringRepo: ILanguageStringRepo
): Resources(res.assets, res.displayMetrics, res.configuration) {
#Throws(Resources.NotFoundException::class)
override fun getString(id: Int): String {
val value = getStringFromRepository(id)
return value ?: super.getString(id)
}
}
Does anyone know how I can get the same functionality without overwriting the Resources class?
Thank you very much for your help :)
I was looking into the same thing some time ago, in the end our team decided to go with Lokalise SDK.
From what I found out, overriding resources is the only way to do it. And even then it still doesn't cover all the cases, like mentioned in Lokalise documentation:
Some views are not supported when inflating from XML (Action bar, Menu
items, Android preferences, may be others), but you can still get the
latest translations via getString(), getText(), getQuantityString()
and other system methods, and set the content of these views
programmatically.
I saw a similar implementation in this library https://github.com/hamidness/restring although it wasn't nearly as complete as Lokalise. You can see how Lokalise is implemented if you include their library and switch to Project view in Android Studio, expand External Libraries and find com.lokalise.android, then you can see the decompiled .class files:
As for the constructor being deprecated - they deprecated it for the purpose of recreating the Resources when you need them for a different Configuration. But Context.createConfigurationContext doesn't let you override the source of the strings provided by resources, so I don't see any alternative.

Access Kotlin Standard Library from Swift

In my Kotlin Multiplatform project, I'm trying to access Kotlin types defined in kotlin-stdlib from Swift.
TL;DR: StdLib types/methods seem not to result in header definitions, I'd like a solution that doesn't involve writing lots of boilerplate code
My scenario
I have an interface defined in Kotlin ...
interface MyKotlinInterface {
fun run() : Sequence<String>
}
... and implemented this interface in Swift ...
class MySwiftClass : MyKotlinInterface {
func run() -> KotlinSequence {
// return sequenceOf("foo")
}
}
... there I'm trying to create a Sequence but there are no methods from the kotlin.sequences package available (e.g. generateSequence).
Is it actually possible to access Kotlin framework types or methods beyond what I define in my code -- if yes, how? Furthermore, how can this be achieved without writing boilerplate code?
Further details
Having a look into the generated Objective-C header file, I see definitions for my class (obviously) and basic Kotlin types. What's missing is basically everything from the standard library functionality (I care for everything Sequence-related).
My build.gradle.kts looks like:
plugins {
kotlin("multiplatform") version "1.3.0"
}
kotlin {
targets { /* ... */ }
sourceSets {
getByName("commonMain") {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib-common")
}
}
// ...
getByName("iosMain") {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib")
}
}
}
}
Having the kotlin-stdlib defined as a dependency for the iOS target, I would expect those to become actually available from Swift.
Minimal working example
https://github.com/panzerfahrer/so-mwe-kotlin-mpp-swift
Current solution approach
The only solution I came up with, is writing the desired function for the iOS target:
fun <T : kotlin.Any> generateSequence(nextFunction: () -> T?): kotlin.sequences.Sequence<T> = kotlin.sequences.generateSequence(nextFunction)
This works ok-ish but is highly unsatisfying as it requires lots of boilerplate code. Additionally, extension functions cannot be made available this way and would require more boilerplate code or even rewriting parts of the standard library.
Desired solution
I like to avoid writing boilerplate code as much as possible. What I actually only care about, is to have (in my case) Sequence fully accessible from Swift. My feeling is, it would be sufficient to make the compiler generate selected or all header definitions for the standard library functionality.
Do you really need lazy computation (aka Sequence) in your Kotlin code?
If no, I would recommend using List<T> instead (and it maps to Swift directly).
For Sequence implementation, a workaround could be to export a factory function from your Kotlin library, e.g. you may declare a function like
fun <T : kotlin.Any> generateSequence(nextFunction: () -> T?)
= kotlin.sequences.generateSequence(nextFunction)
You may select any other factory function for Sequence, that matches your use-case.
In general, there are too many functions in the Kotlin standard library. Exporting them all to Swift will create too many useless symbols in the binary and increase the compilation time.

Which one should I use between it and this in kotlin when I use extensions class?

ScreenDef is a class, I add a function setDevice for the class, which one is correct between Code A and Code B? why?
I think that Code B is correct, right?
Code C
data class ScreenDef(
val brightness: Int
): DeviceDef
class ScreenHelper(val mContext: Context) {
fun setScreen(aScreenDef: ScreenDef){
}
}
Code A
fun ScreenDef.setDevice(mContext: Context) {
ScreenHelper(mContext).setScreen(this)
}
Code B
fun ScreenDef.setDevice(mContext: Context) {
ScreenHelper(mContext).setScreen(it)
}
You should use this. it is referred as shorthand if there is only one parameter in lambdas.
context?.let {
it.resources.getInt(R.int.anyint) // just for example
}
In above snippet, it is the shorthand for lamda parameter(in case of only one parameter).
context?.let { cxt -> // here we have manually defined a parameter
cxt.resources.getInt(R.int.anyint) // just for an example
}
In this snippet, instead of it we have created cxt that is exactly same as it.
Actually you are taking the concept of Extension function wrong.
You are creating a data class ScreenDef and want to create an extension function to it, why? If you really want to have a member function just create a normal class and have a function in it.
Extension function should be created when target class is not maintained by you. For example: Activity, Fragments are not maintained by you and if you want to add a custom function, you have to extend them and do it. So to prevent it extension function comes into picture and they are really handy that's why we love it.
You can rather argue, whats wrong with creating extension function for a class created by us. It may or might not be true. It actually depends.
Let's take an example, suppose we have developed a library to draw simple symbols on canvas and there are several function we have created. It turned out to be so good that people are using it, we decided to created advanced version, that can draw more complex symbols that requires using our already developed simple lib. So when we extend the classes of simple lib we might need some functionality to improve some thing etc. in that case if we have imported our simple lib as dependency then its good to create extension function otherwise we would have to create one more child of that class and create desired function. If we have import our lib as source code, we can just go to the source fine and create a function inside it.
I hope it helps.

Categories

Resources