Blog Infos
Author
Published
Topics
, , , ,
Published

The problem

Wouldn’t it be great if we could forget about old Android versions and just use the latest libraries and features?

According to apilevels.com, most users today run Android 10 (SDK 29) or higher. But if your app has millions of users, dropping support for older devices could mean losing a big chunk of your audience — and therefore revenue.

Android itself offers excellent backward compatibility. For example, Jetpack Compose supports all the way back to Android 5 (SDK 21), released in 2014. However, not every library is so generous. Some require you to increase your app’s minSdkVersion in order to use them.

This creates a tough question:

“Do I add this library to get a new feature, but lose 5% of my users?”

In most cases, the answer is: No.

Solution

Fortunately, there’s a workaround. You can integrate a library with a higher minSdkVersion than your app while still providing a fallback solution for users on unsupported devices.

Let’s walk through an example using the Jetpack PDF library, which currently only supports devices with SDK 31 or higher.

Suppose your app has minSdkVersion = 24. If you try to add the library:

dependencies {
    implementation("androidx.pdf:pdf-viewer-fragment:1.0.0-alpha10")
    implementation("androidx.fragment:fragment-compose:1.8.9") # Needed for Pdf Viewer Fragment
}

You’ll hit this build error:

uses-sdk:minSdkVersion 24 cannot be smaller than version 31 declared in library [androidx.pdf:pdf-viewer:1.0.0-alpha10] 
/path/pdf-viewer-1.0.0-alpha10/AndroidManifest.xml as the library might be using APIs not available in 24
Suggestion: use a compatible library with a minSdk of at most 24, or increase this project's minSdk version to at least 31, or use tools:overrideLibrary="androidx.pdf" to force usage (may lead to runtime failures)

The last suggestion is the key. We can override the library’s minSdkVersion in our AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-sdk
        android:minSdkVersion="24"
        tools:overrideLibrary="androidx.pdf" />

<!-- Other configurations -->

</manifest>

But when you build again, you’ll get another error for androidx.pdf.document.service. Add that too, and eventually you’ll need to include multiple overrides:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <uses-sdk
        android:minSdkVersion="24"
        tools:overrideLibrary="androidx.pdf, androidx.pdf.document.service, androidx.pdf.viewer.fragment" />

    <!-- Other configurations -->

</manifest>

At this point, the project will compile successfully.

Adding Runtime Checks

Now we need to make sure our app behaves correctly on devices below SDK 31.

In the screen where the user selects and opens a PDF, we add a runtime SDK version check in rememberLauncherForActivityResult.

  • If the device is running SDK 31+, we use the Jetpack PDF library to render the PDF.
  • Otherwise, we fall back to an intent that delegates PDF rendering to another installed app.

This way:

  • Users with newer devices get the modern in-app PDF viewer.
  • Users with older devices still get the feature — just via an external app.
package com.example.pdfapp
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.os.bundleOf
import androidx.fragment.app.FragmentActivity
import androidx.fragment.compose.AndroidFragment
import androidx.pdf.viewer.fragment.PdfViewerFragment
import com.example.pdfapp.ui.theme.PdfAppTheme
class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
PdfAppTheme {
Scaffold(
modifier = Modifier
.fillMaxSize()
.safeContentPadding()
) { innerPadding ->
Surface(modifier = Modifier.padding(innerPadding)) {
DocumentSelectionScreen()
}
}
}
}
}
}
@Composable
private fun DocumentSelectionScreen() {
val context = LocalContext.current
var documentUri: Uri? by remember { mutableStateOf(null) }
val documentSelectionLauncher =
rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
if (Build.VERSION.SDK_INT >= 31) {
documentUri = uri
} else {
val openPdfIntent = Intent(Intent.ACTION_VIEW).apply {
setDataAndType(uri, "application/pdf")
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
context.startActivity(openPdfIntent)
}
}
if (documentUri == null) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize()
) {
Button(
onClick = { documentSelectionLauncher.launch(arrayOf("application/pdf")) }
) {
Text(stringResource(R.string.select_document))
}
}
} else {
documentUri?.let { PdfRenderer(uri = it, modifier = Modifier.fillMaxSize()) }
}
}
@Composable
private fun PdfRenderer(
uri: Uri,
modifier: Modifier = Modifier
) {
AndroidFragment<PdfViewerFragment>(
arguments = bundleOf(
"documentUri" to uri
),
modifier = modifier
)
}
view raw MainActivity.kt hosted with ❤ by GitHub

Result: no one is left behind. 🎉

Demo Project

You can see the full implementation in my demo app here:
👉 GitHub: pdf-app

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Kobweb:Creating websites in Kotlin leveraging Compose HTML

Kobweb is a Kotlin web framework that aims to make web development enjoyable by building on top of Compose HTML and drawing inspiration from Jetpack Compose.
Watch Video

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kobweb

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author o ...

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kob ...

Jobs

Closing

If you found this article helpful or interesting, please give it a clap and consider subscribing for more content! I’d love to hear your thoughts! Your feedback and insights are always welcome, as I’m eager to learn, collaborate, and grow with other developers in the community.

Have any questions? Feel free to reach out!

You can also follow me on Medium or LinkedIn for more insightful articles and updates. Let’s stay connected!

This article was previously published on proandroiddev.com

Menu