Nanokt
A lightweight extensions library for Android and Kotlin
Install / Use
/learn @conena/NanoktREADME
NanoKt
Introducing NanoKt – an elegant and lightweight solution for streamlined Android development that offers an extensive collection of up-to-date extension functions covering many areas of the Android framework and the Java and Kotlin standard libraries. NanoKt will make your code more elegant, efficient and save you time in the process. Moreover, this is achieved without compromising performance, as almost all provided functions and properties are inlined at compile time.
Why yet another extension library?
NanoKt stands out by providing well-documented functions with recommended annotations for an AndroidX-like experience. The thoughtful structure and method naming, inspired by the Android framework, aims to eliminate redundant code without enforcing a one-size-fits-all framework. It is designed to integrate seamlessly into various Android projects. I've been using it for the entire - more than a year - beta phase in production for several of my apps (including [Logcat Reader Professional][LRP link]) and plan to actively develop it further and integrate it into all my projects.
The library is divided into modules for pure Kotlin, Kotlin-JVM, and Kotlin-Android, making it compatible with a wide range of projects. I plan to release a compatible version for Kotlin Multiplatform at a later date.
Table of contents
- Getting started
- Examples
- Copying to the clipboard
- Accessing system services
- Reading configuration (e.g. night mode)
- Reading system settings (e.g. airplane mode))
- Starting activities
- Starting the Play Store
- Starting other apps (e.g. settings, mail client, etc.)
- Conversion of complex units
- Working with SharedPreferences
- Accessing theme attributes
- Working with bundles
- Working with services
- Working with views
- Working with bitmaps
- Encoding/Decoding Base64
- Debug logging
- Other examples
- Experimental parts of the library
- Versioning
- FAQ
- Contribution
- License
Getting started
To start using NanoKt in your project, add the appropriate dependencies to your build.gradle file.
repositories {
mavenCentral()
}
dependencies {
// Extensions for Android
implementation 'com.conena.nanokt:nanokt-android:1.3.0'
// Pure Kotlin Extensions
implementation 'com.conena.nanokt:nanokt:1.3.0'
// Extensions for the Java standard library
implementation 'com.conena.nanokt:nanokt-jvm:1.3.0'
// Extensions for threads in Android. Not needed if you use coroutines
implementation 'com.conena.nanokt:nanokt-android-threads:1.3.0'
}
Examples
Below are some examples of how NanoKt takes the pain out of writing boilerplate code and follows current Android documentation recommendations. Examples of the Android-independent NanoKt-JVM and NanoKt (Kotlin) are not listed here yet. You can browse the repository and look at the extensions offered.
Copying to the clipboard
It is requested to copy plain text to the clipboard and notify the user about the copying, but without displaying the text, e.g. because it is an access code.
Solution without NanoKt
val clipboardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
val clipData = ClipData.newPlainText(null, textToCopy)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
clipData.description.extras = PersistableBundle().apply {
putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
}
}
clipboardManager.setPrimaryClip(clipData)
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
Toast.makeText(this, "Access code copied.", Toast.LENGTH_SHORT).show()
}
Solution with NanoKt
clipboardManager.setPrimaryClip(text = textToCopy, isSensitive = true) {
toastShort("Access code copied.")
}
The example takes into account the [current recommendations on user feedback when copying text to the clipboard][clipboard feedback], as well as the changes in Android 13. The trailing lambda with the toast is only invoked on devices with Android versions below Android 13.
Accessing system services
You may wonder where the clipboard manager comes from in the first example. A brief look at the source code provides the answer.
@get:CheckResult
@get:MainThread
inline val Context.clipboardManager get() = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
NanoKt makes all available services available as context extension properties. Frequently used services are available directly in the context, less frequently used services have to be called via a systemServices extension. Since the ClipboardManager can only be obtained on the MainThread, this is indicated with an annotation so that you receive a warning in your IDE if you use it incorrectly. Additionally, you don't have to worry about nullability because NanoKt takes care of that.
// Examples for commonly used services
context.clipboardManager
context.layoutInflater
context.notificationManager
// All services available via systemServices
context.systemServices.midiManager
context.systemServices.printManager
// Nullability is respected. DevicePolicyManager is null in instant apps
context.systemServices.devicePolicyManager?.storageEncryptionStatus
Reading configuration (e.g. night mode)
Solution without NanoKt
val nightMode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
resources.configuration.isNightModeActive
} else {
val flag = resources.configuration.uiMode.and(Configuration.UI_MODE_NIGHT_MASK)
flag == Configuration.UI_MODE_NIGHT_YES
}
Solution with NanoKt
// configuration is an extension on Context
val nightMode = configuration.isNightModeActiveCompat
Further examples of available configuration extensions:
val isTablet = configuration.isTablet()
val isLargeTablet = configuration.isTablet(requireXLarge = true)
val isCar = configuration.isCar
val isTelevision = configuration.isTelevision
val isWatch = configuration.isWatch
val isVrHeadset = configuration.isVrHeadset
val isLongScreen = configuration.isLongScreen
val isLandscape = configuration.isLandscape
val isPortrait = configuration.isPortrait
val isLtrLayout = configuration.isLtrLayout
val isRtlLayout = configuration.isRtlLayout
Reading system settings (e.g. airplane mode)
Solution without NanoKt
val isAirplaneModeEnabled = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
try {
Settings.Global.getInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON)
} catch (_: Throwable) {
0
} == 1
} else {
@Suppress("DEPRECATION")
try {
Settings.System.getInt(contentResolver, Settings.System.AIRPLANE_MODE_ON)
} catch (_: Throwable) {
0
} == 1
}
Solution with NanoKt
val isAirplaneModeEnabled = settings.isAirplaneModeEnabled
Via the Context extension property "settings" NanoKt provides a variety of system settings to read and convenient functions to easily load additional settings as needed.
val isUSBDebuggingEnabled = settings.isAdbEnabled
val isBluetoothEnabled = settings.isBluetoothEnabled
val isDataRoamingEnabled = settings.isDataRoamingEnabled
val areDeveloperOptionsEnabled = settings.isDeveloperOptionsEnabled
val deviceName = settings.deviceName
val isMobileDataEnabled = settings.isMobileDataEnabled
val isWifiEnabled = settings.isWifiEnabled
val currentBrightness = settings.screenBrightness
settings.getGlobalIntOrNull(name = settingName)
settings.getSystemIntOrNull(name = settingName)
settings.getSecureIntOrNull(name = settingName)
...
Starting activities
Tired of the same old routine, rewriting activity-starting code and worrying about forgetting essential flags like FLAG_ACTIVITY_NEW_TASK? With NanoKt, this problem is a thing of the past because the new task flag is added automatically when called from a non-activity context.
// start an activity
context.startActivity<MyActivity>()
// start an activity with options
context.startActivity<MyActivity>(options = startOptions)
// start an activity with a custom intent
startActivity<MyActivity>() {
putExtra(SOME_BUNDLE_EXTRA, "extra")
// Set flags with extension properties
isNew
