I am trying to use #ChecksSdkIntAtLeast annotation to convert our java utility functions into kotlin properties that would not require #SuppressLint("NewApi").
But anyway I write the property, it stops red underlining code for both branches of if.
Demo:
#get:ChecksSdkIntAtLeast(api = Build.VERSION_CODES.O)
val isAtLeastOreo: Boolean
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
manager.getNotificationChannel("general")
} else {
manager.getNotificationChannel("general") // CORRECTLY UNDERLINED
}
if (isAtLeastOreo) {
manager.getNotificationChannel("general")
} else {
manager.getNotificationChannel("general") // NOT UNDERLINED - WHY??
}
Same thing happens if I make it into function instead of getter. The only working one would be using lambda, but then I am losing if-else construct and would require massive amount of places to refactor.
Issue has been reported on Aug 23. Waiting for response
https://issuetracker.google.com/issues/197428342
Related
I am working on a project and I noticed that this check if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { only takes care of API level 30 and above for API level 29 and below it does not. I however don't know the correct flags to use to like handle this case. For instance else {... showStatuBar and ... show navigationBars} any ideas on how to show status bar and navigation bar on API level 29 and below.
private fun showUI() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
activity?.window?.setDecorFitsSystemWindows(true)// ensures the layout fits system window
activity?.window?.insetsController?.show(
WindowInsets.Type.statusBars()
or WindowInsets.Type.navigationBars()
)
} else {
// What to add
}
}
The Android oficial documentation's all version codes documented take a look at the link bellow:
Version Codes
Follows a small example bellow, just try to adapt at will:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
//Your code go here
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOOLIPOP) {
//Your code go here
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
//Your code go here
}
Or like the code versions are just a Int value you can use ranges and when instead if:
when (Build.VERSION.SDK_INT) {
>= 30 -> {
//Your code go here
}
in 26 .. 29 -> {
//Your code go here
}
in 21 .. 25 -> {
//Your code go here
}
}
References:
https://www.programiz.com/kotlin-programming/if-expression
https://kotlinlang.org/docs/ranges.html
https://www.programiz.com/kotlin-programming/when-expression
https://typealias.com/start/kotlin-conditionals/
I'm trying to implement TextToSpeech in my Android Application:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
TTS.speak(toSpeak, TextToSpeech.QUEUE_FLUSH, null, null)
}
else {
TTS.speak(toSpeak, TextToSpeech.QUEUE_FLUSH, null)
}
I'm already providing an if clause for the case it's Lollipop or older, but I'm getting the following error:
speak(String!, Int, HashMap<String!, String!>!: Int' is deprecated. Deprecated in Java.
I don't know what I should use instead of TextToSpeech.QUEUE_FLUSH.
It's just a false warning in the IDE that shouldn't be there. For some reason it doesn't see that you did the code correctly as far as checking the API and applying the correct method signature.
I have some code that compiles successfully using ViewTreeObserver#removeOnGlobalLayoutListener(...) and when it runs, this method throws NoSuchMethodError. Why?
There are two methods in ViewTreeObserver with almost the same name.
removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener victim)
(on then global) is a method that was added in API 16. It replaces
removeGlobalOnLayoutListener(ViewTreeObserver.OnGlobalLayoutListener victim)
(global then on) which has existed since API 1, but which is now deprecated.
Both methods can appear present at compile-time (if you're building against Jellybean or higher) but the newer one will fail on pre-Jellybean devices.
This code thwarts the error:
try {
thing.removeOnGlobalLayoutListener(victim);
} catch (NoSuchMethodError x) {
thing.removeGlobalOnLayoutListener(victim);
}
So does this code:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
thing.removeGlobalOnLayoutListener(victim);
} else {
thing.removeOnGlobalLayoutListener(victim);
}
I assume you are talking about removeOnGlobalLayoutListener from ViewTreeObserver class. This method was added in API level 16. My best guess is that you try to use it on a device running an old version of Android that's why it can't be found.
I have working code
public static void removeOnGlobalLayoutListener(View v, ViewTreeObserver.OnGlobalLayoutListener listener){
if (Build.VERSION.SDK_INT < 16) {
v.getViewTreeObserver().removeGlobalOnLayoutListener(listener);
} else {
v.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
}
Im developing an app with the latest android version (4.2.1 API-Level 17) for tablets with multiuser capabilities.
I want to restrict certain features (like the access to the app preferences) to the owner of the tablet (that is the user who can add and remove other user accounts)
is there any way i can find out if the current user is the owner?
i read through the UserManager and UserHandle API docs but couldn't find a function that allows me to check for it.
have i missed something or is there another way to do that?
Similar but without reflection:
static boolean isAdminUser(Context context)
{
UserHandle uh = Process.myUserHandle();
UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
if(null != um)
{
long userSerialNumber = um.getSerialNumberForUser(uh);
Log.d(TAG, "userSerialNumber = " + userSerialNumber);
return 0 == userSerialNumber;
}
else
return false;
}
You can create an extension property in Kotlin to make it simpler:
val UserManager.isCurrentUserDeviceOwner: Boolean
get() = if (SDK_INT >= 23) isSystemUser
else if (SDK_INT >= 17) getSerialNumberForUser(Process.myUserHandle()) == 0L
else true
Then, using it is as simple as the following:
val userManager = context.getSystemService(Context.USER_SERVICE) as UserManager
if (userManager.isCurrentUserDeviceOwner) TODO() else TODO()
You can further reduce boilerplate by using global system services definitions that makes userManager and other Android System Services available anywhere in your Kotlin code, with code included in this library I made: https://github.com/LouisCAD/Splitties/tree/master/systemservices
After researching further i found out that the multiuser api is not functional yet, it cant really be used for anything. there is a hack though for checking if the user is the owner using reflections:
public boolean isCurrentUserOwner(Context context)
{
try
{
Method getUserHandle = UserManager.class.getMethod("getUserHandle");
int userHandle = (Integer) getUserHandle.invoke(context.getSystemService(Context.USER_SERVICE));
return userHandle == 0;
}
catch (Exception ex)
{
return false;
}
}
This works for me on the Nexus 7 and Nexus 10 with Android 4.2.1
Its very dirty. so i wouldnt recommend using it unless you are making an app thats device and version specific
So the code is following:
public static ActionBarHelper createInstance(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return new ActionBarHelperICS(activity);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
return new ActionBarHelperHoneycomb(activity);
} else {
return new ActionBarHelperBase(activity);
}
}
If my device is 2.3 (api 9) it shouldn't support Build.VERSION_CODES.ICE_CREAM_SANDWICH but yet it runs, why is that? If it had been a method I called it would have crashed. Is it because it is a public static final int and therefor added from to my code? Please give some general detail and not just a yes/no answer :)
As Stefan pointed out, the api level used to compile the code determines the constants visible at compile time. For my case this doesn't give any problems as I just compare simple values. This only give problems if you use a set method that uses different constants and you happent to use a constant that wasn't possible to handle for your api version.