I'm converting part of an app to kotlin and I've a problem caused by the intellisense of Android Studio (3.5.3) not showing all methods of googleMaps
this is an example:
override fun onMapReady(googleMap: GoogleMap) {
map = googleMap
map.setBuildingsEnabled(true)
map.setTrafficEnabled(true)
map.setOnMapLoadedCallback(OnMapLoadedCallback {
if (currentMission != null) {
drawMission()
} else {
drawNoMission()
}
})
}
for instance I can't see map.setBuildingsEnabled in intellisense, see image
but if I force to call that hidden method, the app still builds, so it's something caused by kotlin or intellisense
It may be related to the gray suggestion I got regarding property access maybe (but this isn't a property because on google maps there isn't a getBuildingsEnabled
did anyone knows how to fix this annoying problem? I don't want kotlin to hide methods that may be useful to me, thanks.
Methods that follow the Java conventions for getters and setters (no-argument methods with names starting with ‘get’ and single-argument methods with names starting with ‘set’) are represented as properties in Kotlin.
In other words if you had a Java method setTrafficEnabled(true) Kotlin will provide you a property access syntax isTrafficEnabled = true. This is one of Kotlin advantages.
If you ignore Kotlin property access syntax and use getters and setters, it will work all the same.
Related
I'm using #RestrictTo annotation to denote that a function should be used in only in subclasses or tests.
To do that I use the following syntax:
#RestrictTo(value = [SUBCLASSES, TESTS])
public override fun onCleared() {
// Expose protected fun onCleared for tests
}
At first it seemed to be working but my teammates reported Android Studio showing this warning:
I could reproduce this after building the project again.
This error goes away if I remove the TESTS scope from the annotation as if the annotation does not support multiple scopes in values.
Do you think if this is the intended behavior of the annotation?
Can you think another way to restrict a function to the union of two different scopes?
Thanks in advance
I am generating documentation using KDoc/Dokka for an android library.
I have a custom view, which extends LinearLayout.
The problem is that LinearLayout contains hundreds of public methods. Dokka generates empty documentation for all of these methods, even though I did not use or override them in my own code.
This completely buries any of my own methods and makes the documentation near useless.
How can I prevent dokka from generating documentation for inherited methods?
Currently this is not supported, probably we will add some flag to turn it on/off.
You can follow this issue: https://github.com/Kotlin/dokka/issues/1501
From the answer by #andrzej-ratajczak the following can be used
pluginsMapConfiguration.set(
["org.jetbrains.dokka.base.DokkaBase": """{ "separateInheritedMembers": true}"""]
)
here an example of my own module
dokkaHtml {
moduleName = "${project.name}"
dokkaSourceSets {
configureEach {
// Suppress a package
perPackageOption {
// will match all packages and sub-packages
matchingRegex.set(".*\\.internal.*")
suppress.set(true)
}
// separate inherited members to avoid polluting our public API
// https://github.com/Kotlin/dokka/issues/1501
pluginsMapConfiguration.set(
["org.jetbrains.dokka.base.DokkaBase": """{ "separateInheritedMembers": true}"""]
)
}
}
}
My ViewModel class implements LifecycleObserver.
When I call fragment.lifecycle.addObserver(this) it produces exception.
Caused by: java.lang.IllegalArgumentException: The observer class has some methods that use newer APIs which are not available in the current OS version. Lifecycles cannot access even other methods so you should make sure that your observer classes only access framework classes that are available in your min API level OR use lifecycle:compiler annotation processor.
Strange, that firstly it was working fine, but not long ago this exception has appeared. I've found, that audioFocusRequest is cause of this bug.
private val audioFocusRequest by lazy {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
.setOnAudioFocusChangeListener(this)
.build() else throw RuntimeException("Can't be done for Android API lower than 26")
}
Does anybody know how it can be fixed?
UPD
Tried to use annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version", but got compilation error:
(decided to paste screenshot, because whole logs are quite big)
UPD 2
At the end I've decided to delete audioFocusRequest field and to use old deprecated method - requestAudioFocus(OnAudioFocusChangeListener l, int streamType, int durationHint) instead of recommended requestAudioFocus(#NonNull AudioFocusRequest focusRequest)
It helped me to make code working again, so it can be solution. But I didn't find answer - why this problem had appeared. It strange because code used to be working before.
So problem has been solved but question still stays unanswered
Try to use kapt "androidx.lifecycle:lifecycle-compiler:2.0.0"
The class which implements LifecycleObserver has some method, which has parameters with type that only exist for higher APIs.
Your variables (i guess) and function parameters must exist on all APIs even function is not called (maybe this is requirement for classes who implement LifecycleObserver).
A possible solution is to change function parameter type to Any (kotlin) or Object (Java) and cast it to appropriate type inside function.
I have to remove this set method on SpinnerView: lifecycleOwner = viewLifecycleOwner
I was able to fix this by moving the offending methods into another class, but still called from my LifecycleObserver. After reading the error message again:
Caused by: java.lang.IllegalArgumentException: The observer class has some methods that use newer APIs which are not available in the current OS version. Lifecycles cannot access even other methods so you should make sure that your observer classes only access framework classes that are available in your min API level OR use lifecycle:compiler annotation processor.
It seems as though no methods or objects are allowed in the class extending LifecycleObserver if they don't exist in the device's OS, even if they are wrapped in an SDK version check and never accessed.
I use kotlinx.serialization on Kotlin native project, I a defined Super class for my models and all of the models extends from it.
I defined a function to called toJSON() for serialize variables and fields inside model that all of class models have it.
#Serializable
open class Model {
fun toJSON(): String = JSON.stringify(this);
}
And I created a subclass
class Me : Model() {
var name:String = "Jack";
}
but when I invoke JSON.stringify(this), IDE get a Warning to me:
This declaration is experimental and its usage must be marked with '#kotlinx.serialization.ImplicitReflectionSerializer' or '#UseExperimental(kotlinx.serialization.ImplicitReflectionSerializer::class)'
I paid attention and I used #ImplicitReflectionSerializer annotation while not worked.
Where is my problem?
This is discussed here. It's the particular overload you're using which is still experimental. So your options are either to use the other overload (which takes in a serializer) or to use one of the annotations mentioned in the error message. If you look at the answer to the question I linked (and the comments following it), you'll see it talks about using #UseExperimental and where it should be used.
So, I have the very simple problem, that I cannot seem to format a double coming from an ObservableField in a data binding. I have the following layout:
android:text='#{String.format("%.2f€", transaction.value)}'
and here the definition of transaction.value:
public final ObservableField<Double> value = new ObservableField<>();
I always get this error:
java.util.IllegalFormatConversionException: f != android.databinding.ObservableField
I get the same issue if I use an ObservableDouble. The only way to avoid this seems to be if I call transaction.value.get() in the binding, but I was under the impression that get/set can be omitted here, as I am successfully doing e.g. with an ObservableField<Date>.
I am targeting Sdk 26 with with buildTools 26.0.2.
Update 1
I tried the same thing now with my previous setup, and it worked, just as I remembered. So I pinpointed it to the Gradle version change from 2.3.1 to 3.0.1 (even when I put in target/compile Sdk 26 and buildToolsVersion 26.0.2 with Gradle 2.3.1 it works).
As #dominicoder pointed out to look into the generated data bindings, here is the difference, explaining the problem:
Gradle 2.3.1:
android.databinding.ObservableField<java.lang.Double>transactionValue=null;
java.lang.Double transactionValueGet=null;
....
if(transaction!=null){
// read transaction.value
transactionValue=transaction.value;
}
updateRegistration(3,transactionValue);
if(transactionValue!=null){
// read transaction.value.get()
transactionValueGet=transactionValue.get();
}
// read String.format("%.2f€", transaction.value.get())
stringFormatJavaLangString2fTransactionValue=java.lang.String.format("%.2f€",transactionValueGet);
Gradle 3.0.1:
android.databinding.ObservableField<java.lang.Double> transactionValue = null;
// NO transactionValueGet field !!!
....
if(transaction!=null){
// read transaction.value
transactionValue=transaction.value;
}
updateRegistration(3,transactionValue);
// read String.format("%.2f€", transaction.value)
stringFormatJavaLangString2fTransactionValue=java.lang.String.format("%.2f€",transactionValue);
So - it looks like this is a bug introduced in that version update (to be safe, I created a completely new project with a similar setting, a TextView having its text bound to an ObservableField<Double> using String.format()). Or maybe it is intended behavior, but I really wouldn't understand the purpose of that kind ob breaking change.
Update 2
Currently the problem is filed in the Android issue tracker
The Problem
Check the databinding generated java file for your layout. You will find something like this:
android.databinding.ObservableField<java.lang.Double> transactionValue = null;
// read String.format("%.2f€", accountViewModel.test)
stringFormatJavaLangString2fAccountViewModelTest = java.lang.String.format("%.2f€", transactionValue);
You can see that it's using the observable field literally and I think it's because the signature of String.format is (String string, Object args...). Because the observable field is an Object it can be used as is, so the code generator does not try to "get" the double value instead.
The Date formatting example works because the format method has a signature of (Date date). Thus, the code generator realizes that the signature doesn't match, but the observable type does, so it "get"s it for you and you'll see something like this:
java.util.Date transactionDateGet = null;
// read transaction.date.get()
transactionDateGet = transactionDate.get();
androidTextFormatDateFormatGetDateFormatContextFormatTransactionDate =
android.text.format.DateFormat.getDateFormat(
getRoot().getContext()).format(transactionDateGet);
The Solution
As you already indicated, the easiest thing to do is just manually call .get() in your binding. However, I'd recommend avoiding doing logic in your XML, as #elmorabea indicated. Instead, I'd suggest using a view model that encapsulates the display logic:
public class TransactionViewModel {
private final Transaction mTransaction;
public TransactionViewModel(#NonNull Transaction transaction) {
mTransaction = transaction;
}
#Bindable
public String getTransactionValueText() {
return String.format("%.2f€", transaction.getValue());
}
}
Then you have something like this:
android:text='#{viewModel.transactionValueText}'
This is more work as you have to have an extra class for each layout file; you'd have to move your observable fields from your models to the viewmodels; and you'd have to make your view model obvserve the model to propagate the changes up the to view, but the separation of concerns makes it far easier to update, extend, reuse, and maintain in the long run.
Hope that helps!