How to only execute code on certain API level - android

For instance, this code:
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.GINGERBREAD) {
myCalendarView.setOnDateChangeListener(
new OnDateChangeListener() {
#Override
public void onSelectedDayChange(CalendarView view, int year, int month, int dayOfMonth) {
Toast.makeText
(
getApplicationContext(), ""+dayOfMonth, 0
).show();
}
}
);
}
Gives error:
Description Resource Path Location Type Call requires API level 11
(current min is 8):
android.widget.CalendarView#setOnDateChangeListener example.java /example/src/com/example/example line
20 Android Lint Problem
I understand why I get this error compile-time. But is there any way to mark a source Java class to only be used on certain API level-11? Or surround code blocks with a define/similar so the code is late-bound/jitted only on devices above API level-11? What is the best solution to achieve what I want? (Which is to provide an activity with CalendarView on devices capabile of it.)

I'm not sure if this is going to solve your issue,
but what you are using to check version is not working under API 9
(and you are supporting since API 8).
You should use:
if (Build.VERSION.SDK_INT > 9) {
Or as problematic function is API 11, check for "SDK_INT>10"
Then for lint errors on eclipse, do as people comment, disable lint errors or add the #SuppressLint("NewAPi") or the target to that function to 11.

For anyone stumbling upon this much later, conditionally executing code based on the API version at runtime is currently a valid way of supporting different platform versions.
See: https://developer.android.com/training/basics/supporting-devices/platforms#version-codes
Where it says:
Android provides a unique code for each platform version in the Build
constants class. Use these codes within your app to build conditions
that ensure the code that depends on higher API levels is executed
only when those APIs are available on the system.
And gives examples:-
Java:
private void setUpActionBar() {
// Make sure we're running on Honeycomb or higher to use ActionBar APIs
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
ActionBar actionBar = getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
}
}
Kotlin:
private fun setUpActionBar() {
// Make sure we're running on Honeycomb or higher to use ActionBar APIs
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
actionBar.setDisplayHomeAsUpEnabled(true)
}
}

Related

How to handle `is deprecated. Deprecated in Java`?

The code I want to use:
window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED)
There is FLAG_SHOW_WHEN_LOCKED that is deprecated in the API 27 and its alternative setShowWhenLocked added in the API 27
How should I use it properly if the minSdk in my project is 21 and the targetSdk is 33?
I get the warning is deprecated. Deprecated in Java
Even if I handle it this way:
if(Build.VERSION.SDK_INT >= 27) {
setShowWhenLocked(true)
setTurnScreenOn(true)
} else {
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
}
I still get the warning. What is the right way to support both old and new API?
TLDR
1.Use different code for different API versions.
2.Ignore/surpress this warning if you properly proccess all the API versions that your app is created for
3.If there is a new alternative that works for all the API levels - use it
Instruction
Use Build.VERSION.SDK_INT in the condition to behave accordingly to the SDK_INT
Use setshowwhenlocked if SDK_INT>=27 and FLAG_SHOW_WHEN_LOCKED if SDK_INT<27
Surpress the warning
Example with the FLAG_SHOW_WHEN_LOCKED/setShowWhenLocked
if(Build.VERSION.SDK_INT >= 27) {
setShowWhenLocked(true)
setTurnScreenOn(true)
} else {
#Suppress("DEPRECATION")
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
}
But why do we have to surpsess the warning?
The warning exists only because #Deprecated APIs do not have any metadata that would indicate which SDK they were deprecated in. as you can see in this issue.
We can surpress the error because we have properly processed both old api (5-27) and new api (27>)
Warning
Do not surpress these warnings if the code is not properly processed by using if conditions where the right API is used.
Example how you must not do
#Suppress("DEPRECATION")
window.addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)
Your minSdk is 21 and targetSdk is 33
This code will work on devices with 5-27 API (Android 5 - Android 8.1) but will not work on new devices. You must properly handle both conditions.
Example with the Vibrator
The old way to get the vibrator
context.getSystemService(VIBRATOR_SERVICE) as Vibrator
The new way to get the vibrator
val vibrator = if (Build.VERSION.SDK_INT >= 31) {
val vibratorManager =
context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
vibratorManager.defaultVibrator
} else {
#Suppress("DEPRECATION")
context.getSystemService(VIBRATOR_SERVICE) as Vibrator
}
Will show you the warning `'VIBRATOR_SERVICE: String' is deprecated. Deprecated in Java`. Go to the [documentation](https://developer.android.com/reference/kotlin/android/content/Context#vibrator_service) and see that this constant can be used in the API 1-31 so we must. And in both IDE and documentation there is the info about the alternative: `Deprecated: Use android.os.VibratorManager to retrieve the default system vibrator.`. As you can see the [VibrationManager](https://developer.android.com/reference/kotlin/android/os/VibratorManager) is added in the API 31 therefore we must write the different code for different sdk versions
If an alternative is backwards compatible
If an alternative is backwards compatible you can just use it instead of the old way
Example
If you inherit AppCompatActivity in your activity:
class SaveMyLifeActivity : AppCompatActivity()
You can meet the warning startActivityForResult(Intent!, Int): Unit' is deprecated. Deprecated in Java if you call startActivityForResult:
val intent = Intent(this, SaveMyLifeActivity::class.java)
startActivityForResult(intent, 0)
You can press Alt+Q (Default keybind) to see the Context info(it is called this way in the AndroidStudio is you check your keymap) or use the website do see the documentation
Take a look to the words that this method is deprecated and you must use registerForActivityResult instead. This method can be called in any version right now, there are no Added/Deprecated "section" in the documentation.
Question: How have you found this documentation? I google AppCombatActivity startActivityForResult and come to this documentation. There is no word about startActivityForResult.
Answer: Open the context info about this method in the IDE (Alt+Q) and look at the bottom of the Context info . There is a class name where this method is located in (ComponentActivity). You have to google ComponentActivity startActivityForResult instead of AppCombatActivity startActivityForResult

Android version check

I'm not new to Android and I'm well used to the version handling and how to condition it, but when I see this it troubles me...
// Check if we're running on Android 5.0 or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Call some material design APIs here
} else {
// Implement this feature without material design
}
On any device pre lollipop this line would crash the app because the Build.VERSION_CODES.LOLLIPOP field does not exists... so why is this in the recommended solution in the documentation?
I'm really wondering what am I missing?
Well in that case use this
// Check if we're running on Android 5.0 or higher
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
// Call some material design APIs here
} else {
// Implement this feature without material design
}
Build.VERSION_CODES.LOLLIPOP = 21
Well, you must compile your project with the latest SDK version. Your constants are replaced with corresponding integer values during compilation. No matter what version of Android you run the application on - integers are the same
Try this one
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
// Marshmallow+
}else{
//below Marshmallow
}
Note: Build.VERSION_CODES.LOLLIPOP_MR1==22
Build.VERSION_CODES.M==23
Bit a late to answer, but, today I encountered with the same issue on Android Studio 3.4.1
So the workaround is:
Upgrade to the latest Android SDK.
And,
After Marshmallow/Android 6 Build.VERSION_CODES.xxx full names are replaced with initials and some other variations.
So, now for Marshmallow, it will be:
Build.VERSION_CODES.M
And for Nougat:
Build.VERSION_CODES.N
And so on.
Read more about the build version codes here: Android Developer Reference

Android handle deprecated method

This is how I handle a deprecated method:
int layoutDirection ;
if (getContext().getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN_MR1) {
layoutDirection = getLayoutDirection();
}else {
layoutDirection = getResolvedLayoutDirection();
}
The problem is that getResolvedLayoutDirection() has been removed at JELLY_BEAN_MR1 and above. So since my project targets API 20 the method cannot be found and I get an error.
If I keep it foolish:
int layoutDirection = getLayoutDirection();
The project compiles and runs but still I get an error to add either the TargetApi or SuppressLint annotation. getLayoutDirection() docs have:
For compatibility, this will return {#link #LAYOUT_DIRECTION_LTR} if
API version is lower than {#link
android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}.
So suppressing lint should be fine.
Should I suppress the lint error or fix it somehow else (possibly make getResolvedLayoutDirection() accessible)?
The approach to conditionally check whether a method is available is to check the version of Android that the device is running, via Build.VERSION.SDK_INT:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
layoutDirection = getLayoutDirection();
}
else {
layoutDirection = View.LAYOUT_DIRECTION_LTR;
}
And add #TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) to the method containing the above block to indicate to the build tools that, for that individual method, to consider your minSdkVersion to be 17 instead of whatever it normally is, for the purposes of generating Lint warnings and errors.

Check Android build project target API level at runtime [duplicate]

This question already has answers here:
Programmatically obtain the Android API level of a device?
(7 answers)
Closed 8 years ago.
I have an application, and it behaves differently when I run it on API 18 or 19. This is not a problem, and I know why does it happen.
However, I want to write one code that will deal with both the versions.
Is there any way to get in runtime which API my application was built with? Specifically, I would like to get 18 or 19 if I build my application with these APIs.
EDIT
It seems to be a duplicate question. I thought that the BUILD_VERSION is something else, because, when I compiled both the versions to API 18 and 19, and print the version, I receive 18. It looks like another problem (although I specified API 19, it is compiled according to 18).
I found that the problem was in the emulator configurations.
I didn't understand your problem completely. But if you want to check which Build Version your app is working on and then act accordingly the you can use the following.
if(Build.VERSION.SDK_INT == 18 ){
// Do some stuff
}
else if (Build.VERSION.SDK_INT == 19) {
// Do some stuff
}
else{
// Do some stuff
}
The Android docs provides some sample code of how to load bitmaps effectively that handles this problem.
The code defines static methods in a class Utils that it references when it needs to know what platform the app is running on. The benefit of doing this is that you can reuse the function calls rather than rewriting long conditional statements over and over. The code looks like this:
public static boolean hasFroyo() {
// Can use static final constants like FROYO, declared in later versions
// of the OS since they are inlined at compile time. This is guaranteed behavior.
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
}
public static boolean hasGingerbread() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
}
public static boolean hasHoneycomb() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
}
public static boolean hasHoneycombMR1() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR1;
}
public static boolean hasJellyBean() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
}
public static boolean hasKitKat() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
}
android.os.Build.VERSION.SDK_INT
Check this link out
Build Version Codes
So you can use as follows...
int thisDevicesApi =Build.VERSION.SDK_INT;
if (thisDevicesApi <=Build.VERSION_CODES.GINGERBREAD) {
//for example
//do something
}

TargetApi not taken into account

In one of our methods, we use smoothScrolling in a list view. As this method is not available before API Level 8 (FROYO), we used the TargetApi annotation to prevent the method from being called in previous SDK versions.
As you can see, we do use TargetApi annotation both in class definition and in statements that use the objects of the class. This is more than needed.
Our problem is that the TargetApi annotation is not taken into account and make our emulator crash in version ECLAIR (SDK 7). By tracing, we just realize that the code that should only be executed in versions 8+ is also executed in version 7.
Are we missing something?
This code is in a listener :
#TargetApi(8)
private final class MyOnMenuExpandListener implements OnMenuExpandListener {
#Override
public void onMenuExpanded( int position ) {
doScrollIfNeeded( position );
}
#Override
public void onMenuCollapsed( int position ) {
doScrollIfNeeded( position );
}
protected void doScrollIfNeeded( int position ) {
if ( mListViewDocuments.getLastVisiblePosition() - 2 < position ) {
mListViewDocuments.smoothScrollToPosition( position + 1 );
}
}
}
And the listener is registered this way :
#TargetApi(8)
private void allowSmothScrollIfSupported() {
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO ) {
//This if should not be necessary with annotation but it is not taken into account by emulator
Log.d( LOG_TAG, "Smooth scroll support installed." );
folderContentAdapter.setOnMenuExpandListener( new MyOnMenuExpandListener() );
}
}
BTW, we run the code in debug mode, so the issue is not related to obfuscation removing annotations.
#TargetApi does not prevent any code from being run, it is merely for annotating code and preventing compiler errors for new APIs once you know you are only conditionally calling them.
You still need to add something along the lines of
if (Build.VERSION.SDK_INT > 7){
//...
}
With almost one year of more thinking about this, I would like to add a tiny complement to #Guykun 's answer :
The #TargetApi will be only be used by tools to say developers "Hey, don't use this method below XXX android SDK". Typically lint.
So, if you design a method like :
if (Build.VERSION.SDK_INT > 7){
//...
}
then you should add #TargetApi( 7 ) to your method's signature.
BUT, if you add an else statement, and provide an alternative that makes it work for all versions of Android like :
if (Build.VERSION.SDK_INT > 7){
//...
} else {
//...
}
then you should not add #TargetApi( 7 ) to your method's signature. Otherwise, other developers will think they can't use your method belw api level 7, but indeed, it would work for them as well.
So this annotation has to be used, for static analysis, to indicate the minimum api level supported by the method. As in :
#TargetApi( 7 )
public void foo() {
if (Build.VERSION.SDK_INT > 7){
//...
else if (Build.VERSION.SDK_INT > 10){
//...
}
}
and even better, use constants defined in android.Build.VERSION_CODES.*.
BTW, you would have noticed that this is useless for private methods indeed, except to get a cleaner code and help to promote the method public in the future.
To enforce lint error when using a method targeted towards higher Api Level, you can use RequiresApi instead of TargetApi and whenever you'll try to use the method without checking the version code, you'll get compilation error.
This is what the documentation says about RequiresApi
This is similar in purpose to the older #TargetApi annotation, but
more clearly expresses that this is a requirement on the caller,
rather than being used to "suppress" warnings within the method that
exceed the minSdkVersion.

Categories

Resources