Minimum API level annotations

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?

1 Like

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.

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.

Maybe I should make this as a request to Anko.

1 Like

For safe permission request, you must use Support Library

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