Minimum API level annotations


#1

I’m not completely sure if this is the right place to ask the question, but here goes. Here’s some code, snipped a little bit to show just the relevant portions:

fun atLeastAPI(api: Int): Boolean {
    return api <= android.os.Build.VERSION.SDK_INT
}

val hasOverlayPermission: Boolean
    get() = if (atLeastAPI(23)) { Settings.canDrawOverlays(appContext) } else { true }

fun requestOverlayPermission(context: Context) {
    if (!hasOverlayPermission) @TargetApi(23) {
        val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                            Uri.parse("package:" + context.packageName))
        context.startActivity(intent)
    }
}

Settings.ACTION_MANAGE_OVERLAY_PERMISSION is only available on API 23 and above. In this case, it’s safe to call, because hasOverlayPermission returns true on api < 23, so that part of the code won’t execute.

If I manually inline all of this into requestOverlayPermission, Android Studio recognizes this, and compiles without warnings. But with the code as it is, even if I use the inline keyword on the first fun/val, Studio warns me about minimum sdk level, so I need to add the @TargetApi(23) annotation that you see.

I’d like to do away with the annotation. Is there a better way to structure my code so that studio can recognize what I’m doing? Is this something I should bring up in the language design section of this forum or the bug tracker?


#2

Hi, unfortunately, this diagnostics is not smart enough to understand what happens in other functions. It only checks current function body for version checks. And I don’t think anything could be done about this otherwise, it will be too complex and resource heavy. So you should do a version check in requestOverlayPermission or suppress it with annotation.


#3

Hi, unfortunately, this diagnostics is not smart enough to understand
what happens in other functions. It only checks current function body
for version checks. And I don’t think anything could be done about this
otherwise, it will be too complex and resource heavy.

Re: being resource-heavy, what if diagnostics like this were applied only to function calls and vals that are marked as inline?

Or, an alternative would be a way that I could annotate a higher order function to say that its arguments are safe to run on an arbitrary api level. Something like,

@SafeOnAnyApi
fun <T> ifAtLeastApi (api: Int, f: () -> T, g: () -> T) {
    if (api <= android.os.Build.VERSION.SDK_INT) @TargetApi(api) { f() } 
    else { g() }
}

If f and g are just lambdas that only ever get called in that location, maybe the compiler/inspector could inline them and then be able to perform that inspection more efficiently.

But of course an annotation like that won’t work right now because it must be a compile time constant.

At a high level, what I’d like here is a function that makes other functions safe to call on all API levels, and I can annotate just that function instead of having to annotate any place I use the function.


#4

Maybe I should make this as a request to Anko.


#5

For safe permission request, you must use Support Library


#6

Some permissions, like the overlay permission in my example, are impossible to request safely.