https://github.com/kdroidfilter/platform-tools
PlatformTools is a Kotlin Multiplatform library designed to provide platform-specific utilities and tools for managing operating systems, cache directories, and application versioning seamlessly across various platforms.
https://github.com/kdroidfilter/platform-tools
Last synced: about 1 year ago
JSON representation
PlatformTools is a Kotlin Multiplatform library designed to provide platform-specific utilities and tools for managing operating systems, cache directories, and application versioning seamlessly across various platforms.
- Host: GitHub
- URL: https://github.com/kdroidfilter/platform-tools
- Owner: kdroidFilter
- License: mit
- Created: 2025-01-07T19:20:57.000Z (over 1 year ago)
- Default Branch: master
- Last Pushed: 2025-03-17T17:25:20.000Z (over 1 year ago)
- Last Synced: 2025-03-26T15:42:53.811Z (about 1 year ago)
- Language: Kotlin
- Homepage: https://kdroidfilter.github.io/Platform-Tools/
- Size: 369 KB
- Stars: 68
- Watchers: 1
- Forks: 3
- Open Issues: 1
-
Metadata Files:
- Readme: README.MD
- License: LICENSE
Awesome Lists containing this project
README
# PlatformTools
**PlatformTools** is a Kotlin Multiplatform library designed to provide platform-specific utilities and tools for managing operating systems, application installation, and release fetching seamlessly across various platforms. The library is modular and divided into three main components: core, appmanager, and releasefetcher.
---
## ๐ Core Module
The **Core** module of the KMP Platform Tools library provides utilities for detecting the underlying operating system and platform, enabling platform-specific logic in Kotlin Multiplatform (KMP) projects. Below, we will detail the functionality and demonstrate usage of the provided features.
### โ๏ธ Function: `getOperatingSystem`
The `getOperatingSystem` function determines the operating system on which the application is currently running. This function is expected to be implemented for each target platform in a Kotlin Multiplatform project.
#### โน๏ธ Note
While this functionality may appear to contradict the principles of Kotlin Multiplatform by focusing on operating systems rather than platforms, it is important to note that `getOperatingSystem` returns the operating system itself and not the platform. For example, on JVM, JavaScript, or WASM targets, this function will return the operating system being used (e.g., `WINDOWS`, `MACOS`, `LINUX`) rather than distinguishing between different platforms or runtime environments.
```kotlin
fun getOperatingSystem(): OperatingSystem
```
### โ๏ธ Function: `getPlatform`
The `getPlatform` function determines the specific platform or runtime environment on which the application is running. This is useful for small adjustments in shared code that depend on the platform.
```kotlin
fun getPlatform(): Platform
```
### ๐ง Example Usage
Here is an example of how to use `getOperatingSystem` and `getPlatform` with separate `when` blocks for each:
```kotlin
when (val os = getOperatingSystem()) {
OperatingSystem.WINDOWS -> println("Logic for Windows OS")
OperatingSystem.MACOS -> println("Logic for macOS")
OperatingSystem.LINUX -> println("Logic for Linux OS")
OperatingSystem.ANDROID -> println("Logic for Android OS")
OperatingSystem.IOS -> println("Logic for iOS OS")
OperatingSystem.UNKNOWN -> println("Logic for unknown OS")
}
when (val platform = getPlatform()) {
Platform.ANDROID -> println("Logic for Android platform")
Platform.JVM -> println("Logic for JVM platform")
Platform.IOS_NATIVE -> println("Logic for iOS Native platform")
Platform.JS -> println("Logic for JavaScript platform")
Platform.WASM_JS -> println("Logic for WebAssembly JS platform")
Platform.LINUX_NATIVE -> println("Logic for Linux Native platform")
Platform.MAC_OS_NATIVE -> println("Logic for macOS Native platform")
Platform.WINDOWS_NATIVE -> println("Logic for Windows Native platform")
}
```
### ๐ ๏ธ Android and JVM Only functions
#### `getCacheDir`
```kotlin
val cacheDir: File = getCacheDir()
```
#### `getAppVersion`
```kotlin
val version = getAppVersion()
println("App version: $version")
```
### ๐ ๏ธ Android only functions
#### `getAppVersion(packageName: String)`
Under Android, it's possible to take a package name as a parameter to get the version of another application.
```kotlin
val version = getAppVersion("com.sample.anotherApp")
println("App version: $version")
```
---
This library is available on Maven Central. To include this library in your project, add the following dependency to your `build.gradle.kts`:
```kotlin
implementation("io.github.kdroidfilter:platformtools.core:0.2.7")
```
## ๐ Dark Mode Detection Module
The **Dark Mode Detection** module of PlatformTools enhances theme management by introducing a reactive function, `isSystemInDarkMode`, that accurately detects system-wide dark mode settings across all supported platforms.
### โ๏ธ Function: `isSystemInDarkMode`
The `isSystemInDarkMode` function determines whether the system is currently in dark mode. Unlike `isSystemInDarkTheme`, which is not reactive on desktop platforms, `isSystemInDarkMode` provides real-time updates when the system's theme changes.
#### ๐ How It Works
- On **desktop platforms (Windows, macOS, Linux)**, `isSystemInDarkMode` utilizes [JNA (Java Native Access)](https://github.com/java-native-access/jna) to detect and react to dark mode changes dynamically.
- On **other platforms (Android, iOS, JVM, JavaScript, WebAssembly)**, `isSystemInDarkMode` falls back to `isSystemInDarkTheme`, ensuring compatibility across all targets.
- This makes `isSystemInDarkMode` a **fully cross-platform and reactive solution** for detecting dark mode preferences in a Kotlin Multiplatform project.
```kotlin
fun isSystemInDarkMode(): Boolean
```
### ๐ง Example Usage
Here is an example of using `isSystemInDarkMode` in a Jetpack Compose-based UI:
```kotlin
MaterialTheme(
colorScheme = if (isSystemInDarkMode()) darkColorScheme() else lightColorScheme()
) {
// Your UI content
}
```
This allows automatic adaptation of the UI theme based on the systemโs dark mode setting, ensuring a seamless user experience.
### ๐ Comparison with `isSystemInDarkTheme`
| Function | Reactive | Desktop Support | Other Platforms |
|----------|----------|----------------|----------------|
| `isSystemInDarkTheme` | โ No | โ Not reactive | โ
Yes |
| `isSystemInDarkMode` | โ
Yes | โ
Fully supported (via JNA) | โ
Yes (fallback to `isSystemInDarkTheme`) |
---
## ๐จ Adaptive Title Bar Support
On **Windows and macOS**, the application title bar does not automatically update based on dark mode settings. PlatformTools provides solutions for ensuring adaptive title bars.
### ๐ฅ๏ธ Windows Support
For Windows, use the following function to enable an adaptive title bar:
```kotlin
fun Window.setWindowsAdaptiveTitleBar(dark: Boolean = isSystemInDarkMode())
```
Example usage in a Jetpack Compose for Desktop application:
```kotlin
Window(
title = "sample",
state = rememberWindowState(width = 800.dp, height = 600.dp),
onCloseRequest = ::exitApplication,
) {
window.minimumSize = Dimension(350, 600)
window.setWindowsAdaptiveTitleBar()
App()
}
```
### ๐ macOS Support
On macOS, enabling an adaptive title bar requires adding the following configuration in the `gradle.build.kts` file:
```kotlin
compose.desktop {
application {
mainClass = "MainKt"
nativeDistributions {
macOS {
jvmArgs(
"-Dapple.awt.application.appearance=system"
)
}
}
}
}
```
### ๐ง Linux Support
On Linux, the title bar updates correctly with system-wide dark mode settings, so no additional configuration is required.
---
## ๐ฆ Installation
This library is available on Maven Central. To include this module in your project, add the following dependency to your `build.gradle.kts`:
```kotlin
implementation("io.github.kdroidfilter:platformtools.darkmodedetector:0.2.7")
```
---
With `isSystemInDarkMode`, your Kotlin Multiplatform projects can now dynamically react to dark mode changes on all supported platforms. ๐
## ๐ง AppManager Module (Jvm & Android only)
The **AppManager** module of the KMP Platform Tools library provides utilities to manage application-level information and configurations across multiple platforms. Below, we will detail the functionality and demonstrate usage of the provided features.
### ๐ฎ Overview
This module is designed to simplify retrieving and managing app-related metadata, such as package information and other common requirements. It supports platform-specific implementations while offering a unified API for shared code.
### ๐ก Interface: `AppInstaller`
The `AppInstaller` interface provides platform-specific functionality for managing application installation and uninstallation. For each platform, the required file format is as follows:
- **Android**: APK file
- **Windows**: MSI file
- **Linux**: DEB file
- **Mac**: PKG file
#### Key Functions
##### โฌ๏ธ `installApp`
Installs an application from the specified file. This function requires appropriate permissions, such as the ability to install apps from unknown sources.
```kotlin
suspend fun installApp(appFile: File, onResult: (success: Boolean, message: String?) -> Unit)
```
**Parameters:**
- `appFile`: The file to be installed (e.g., APK, MSI, PKG, or DEB).
- `onResult`: A callback with the installation result. Provides:
- `success`: Indicates whether the installation succeeded.
- `message`: An optional message with additional context (e.g., error details).
##### โฌ๏ธ `uninstallApp`
Uninstalls an application using the provided package name or removes the current application. Currently, uninstallation functionality is only supported on Android.
```kotlin
suspend fun uninstallApp(packageName: String, onResult: (success: Boolean, message: String?) -> Unit)
```
```kotlin
suspend fun uninstallApp(onResult: (success: Boolean, message: String?) -> Unit)
```
**Parameters:**
- `packageName` (optional): The package name of the app to uninstall.
- `onResult`: A callback with the uninstallation result. Provides:
- `success`: Indicates whether the uninstallation succeeded.
- `message`: An optional message with additional context.
### ๐ซ Factory Function: `getAppInstaller`
Retrieves the platform-specific implementation of the `AppInstaller` interface.
```kotlin
fun getAppInstaller(): AppInstaller
```
**Example Usage:**
```kotlin
suspend fun performAppInstallation(appFile: File) {
val appInstaller = getAppInstaller()
appInstaller.installApp(appFile) { success, message ->
if (success) {
println("App installed successfully.")
} else {
println("Failed to install app: $message")
}
}
}
suspend fun performAppUninstallation(packageName: String) {
val appInstaller = getAppInstaller()
appInstaller.uninstallApp(packageName) { success, message ->
if (success) {
println("App uninstalled successfully.")
} else {
println("Failed to uninstall app: $message")
}
}
}
```
---
### ๐ง Windows-Specific Configuration
The `AppManager` module provides additional configuration options for managing Windows application installations:
#### ๐ Configuring Administrator Privileges
You can configure whether the application installation requires administrator privileges by modifying the `requireAdmin` property:
```kotlin
WindowsInstallerConfig.requireAdmin = false // Default is true
```
#### ๐ Enabling Per-User Installation for Silent Updates
For enabling silent updates on Windows, it is recommended to configure `perUserInstall` as `true` in your build settings. Below is an example configuration for a Compose Desktop application:
```kotlin
compose.desktop {
application {
mainClass = "com.kdroid.sample.MainKt"
nativeDistributions {
targetFormats(TargetFormat.Msi, TargetFormat.Deb, TargetFormat.Pkg)
windows {
perUserInstall = true
}
}
}
}
```
---
### ๐ฎ Android-Specific Function: `installAppSilently`
The `AppManager` module provides an additional function for Android devices running in Device Owner mode:
#### ๐ `installAppSilently`
Silently installs an application from the specified file without requiring user interaction. This is particularly useful for enterprise environments or Device Owner mode.
```kotlin
suspend fun installAppSilently(
appFile: File,
onResult: (success: Boolean, message: String?) -> Unit
)
```
## ๐ Additional Functions (Jvm & Android)
The `AppManager` module also provides the following utility functions for JVM and Android platforms:
### ๐ `restartApplication`
Restarts the current application.
```kotlin
fun restartApplication()
```
### ๐ `hasAppVersionChanged`
Checks if the application version has changed since the last time it was opened. This is useful for detecting updates and performing related actions.
```kotlin
fun hasAppVersionChanged(): Boolean
```
### ๐ `isFirstInstallation`
Checks if it is the first installation of the app. This function can be used to perform setup actions or show onboarding screens.
```kotlin
fun isFirstInstallation(): Boolean
```
**Example Usage:**
```kotlin
if (hasAppVersionChanged()) {
println("The application has been updated.")
// Perform actions required after an update
} else {
println("No updates detected.")
}
if (isFirstInstallation()) {
println("This is the first installation of the app.")
// Perform any necessary setup actions here
} else {
println("The app has been installed before.")
}
```
This library is available on Maven Central. To include it in your project, add the following dependency to your `build.gradle.kts` :
```kotlin
implementation("io.github.kdroidfilter:platformtools.appmanager:0.2.7")
```
---
## ๐ฅ Release Fetcher
### ๐ง Overview
This module simplifies application downloads for the target platform and streamlines new release management.
### ๐ฆ `Downloader` Class
The `Downloader` class is a core component of the **Release Fetcher** module, designed to download application files from a given URL while tracking the download progress.
### Main Functionality
- **File Downloading:**
- The primary method, `downloadApp`, accepts a URL and downloads the associated file.
- It provides real-time updates on the download progress through a callback `onProgress`, reporting a percentage (from 0.0 to 100.0) and the local file when available.
- In case of an error, the progress percentage is set to `-1.0`.
### ๐ง Technical Highlights
- **Error Handling:** Comprehensive handling of network or file errors with detailed error logging.
- **Optimized Downloading:** Uses a configurable buffer for efficient file writing.
- **Coroutine Compatibility:** The method is `suspend`, making it ideal for use with Kotlin Coroutines.
---
### ๐ง Usage Example
Here is how you can use the `Downloader` class:
```kotlin
fun main() = runBlocking {
val downloader = Downloader()
val url = "https://example.com/app-release.apk"
val success = downloader.downloadApp(url) { progress, file ->
when {
progress == -1.0 -> println("Error during download.")
progress == 100.0 -> println("Download complete: ${file?.absolutePath}")
else -> println("Progress: ${"%.2f".format(progress)}%")
}
}
if (success) {
println("Download succeeded!")
} else {
println("Download failed.")
}
}
```
### โ๏ธ Configuration
The `ReleaseFetcherConfig` object allows modification of configuration settings for the downloader:
- **Default Configuration:**
- `downloaderBufferSize`: `2 * 1024 * 1024` (2 MB)
- `clientTimeOut`: `HttpTimeoutConfig.INFINITE_TIMEOUT_MS`
- **Example:** Modifying the default configuration:
```kotlin
ReleaseFetcherConfig.downloaderBufferSize = 4 * 1024 * 1024 // Set buffer size to 4 MB
ReleaseFetcherConfig.clientTimeOut = 60_000 // Set client timeout to 60 seconds
```
### ๐ง `GitHubReleaseFetcher` Class
The `GitHubReleaseFetcher` class is a key utility in the **Release Fetcher** module that interacts with the GitHub API to fetch release information, check for updates, and retrieve platform-specific download links.
### ๐ง Main Functionality
- **Fetch Latest Release:**
- The `getLatestRelease` method uses the GitHub API to fetch the latest release for a given repository.
- Handles HTTP responses and parses the response body into a `Release` object.
- **Check for Updates:**
- The `checkForUpdate` method compares the latest release version with the current application version.
- The comparison is made using the release name (`tag_name`) of the latest GitHub release and the application's version.
- If a newer version is available, it triggers a callback with the new version and changelog.
- **Platform-Specific Download Links:**
- The `getDownloadLinkForPlatform` method provides the appropriate download link based on the current operating system.
- Supports Android (`.apk`), Windows (`.msi`), Linux (`.deb`), and macOS (`.dmg`).
### ๐ง Important Note
- The GitHub repository must contain at least one release.
- The release name (`tag_name`) in the repository is critical as it is compared to the application's current version.
- Ensure the GitHub repository is configured correctly to include meaningful release names and assets.
---
### ๐ง Usage Example
Here is how you can use the `GitHubReleaseFetcher` class:
```kotlin
fun main() = runBlocking {
val fetcher = GitHubReleaseFetcher(owner = "ownerName", repo = "repoName")
// Fetch the latest release
val latestRelease = fetcher.getLatestRelease()
if (latestRelease != null) {
println("Latest version: ${latestRelease.tag_name}")
println("Changelog: ${latestRelease.body}")
// Get platform-specific download link
val downloadLink = fetcher.getDownloadLinkForPlatform(latestRelease)
if (downloadLink != null) {
println("Download here: $downloadLink")
} else {
println("No suitable download link for the current platform.")
}
} else {
println("Failed to fetch the latest release.")
}
// Check for updates
fetcher.checkForUpdate { latestVersion, changelog ->
println("Update available: $latestVersion")
println("Changelog: $changelog")
}
}
```
This library is available on Maven Central. To include it in your project, add the following dependency to your `build.gradle.kts`:
```kotlin
implementation("io.github.kdroidfilter:platformtools.releasefetcher:0.2.7")
```
---
## โ๏ธ Permission Handler Module (Android Only)
The **Permission Handler** module provides a simple way to check and request permissions on Android. It simplifies the process, requiring only that you declare the necessary permissions in the Android manifest file.
### ๐ง Main Features
- **Permission Checking:**
- Functions like `hasInstallPermission()` allow you to verify if a specific permission is granted.
- **Permission Requesting:**
- Functions like `requestInstallPermission(onGranted, onDenied)` allow you to request permissions, with callbacks for granted or denied states.
- **Manifest Requirements:**
- Each function's documentation specifies the permissions required in the manifest file.
### ๐ง Available Permissions and Functions
1. **Install Permission:**
- **Check:** `hasInstallPermission()`
- **Request:** `requestInstallPermission(onGranted, onDenied)`
- **Manifest Permission:**
```xml
```
2. **Notification Permission:**
- **Check:** `hasNotificationPermission()`
- **Request:** `requestNotificationPermission(onGranted, onDenied)`
- **Manifest Permission:**
```xml
```
3. **Location Permissions:**
- **Check:** `hasLocationPermission()`
- **Request:** `requestLocationPermission(onGranted, onDenied)`
- **Manifest Permissions:**
```xml
```
4. **Background Location Permission:**
- **Check:** `hasBackgroundLocationPermission()`
- **Request:** `requestBackgroundLocationPermission(onGranted, onDenied)` (requires location permissions to be granted first)
- **Manifest Permission:**
```xml
```
5. **Bluetooth Permissions:**
- **Check:** `hasBluetoothPermission()`
- **Request:** `requestBluetoothPermission(onGranted, onDenied)`
- **Manifest Permissions:**
```xml
```
6. **Camera Permission:**
- **Check:** `hasCameraPermission()`
- **Request:** `requestCameraPermission(onGranted, onDenied)`
- **Manifest Requirements:**
```xml
```
7. **Audio Recording Permission:**
- **Check:** `hasRecordAudioPermission()`
- **Request:** `requestRecordAudioPermission(onGranted, onDenied)`
- **Manifest Permission:**
```xml
```
8. **Overlay Permission:**
- **Check:** `hasOverlayPermission()`
- **Request:** `requestOverlayPermission(onGranted, onDenied)`
- **Manifest Permission:**
```xml
```
9. **Contacts Permissions:**
- **Read Contacts:**
- **Check:** `hasReadContactsPermission()`
- **Request:** `requestReadContactsPermission(onGranted, onDenied)`
- **Manifest Permission:**
```xml
```
- **Write Contacts:**
- **Check:** `hasWriteContactsPermission()`
- **Request:** `requestWriteContactsPermission(onGranted, onDenied)`
- **Manifest Permission:**
```xml
```
10. **Read External Storage Permission (Android 13 and Above):**
- **Check:** `hasReadExternalStoragePermission(mediaTypes: Set = emptySet())`
- **Request:** `requestReadExternalStoragePermission(mediaTypes: Set = emptySet(), onGranted, onDenied)`
- **MediaType Enum:**
```kotlin
enum class MediaType {
IMAGES,
VIDEO,
AUDIO
}
```
- **Manifest Permission:**
```xml
```
### ๐ง Usage Example
Here is how to use the module for requesting and handling permissions:
```kotlin
fun checkAndRequestInstallPermission() {
if (hasInstallPermission()) {
println("Install permission already granted.")
} else {
requestInstallPermission(
onGranted = {
println("Install permission granted.")
},
onDenied = {
println("Install permission denied.")
}
)
}
}
```
### ๐น Special Notes for External Storage Permissions
- **Requests read external storage permission for the application.**
- **Behavior Based on Android Version:**
- For Android versions below 13, it requests `READ_EXTERNAL_STORAGE`.
```xml
```
- For Android 13 and above, it requests specific media permissions based on the provided media types.
```xml
```
### ๐ง Usage Example
Here is how you can use the module for requesting and handling External storage permissions:
```kotlin
fun checkAndRequestReadExternalStoragePermission() {
val mediaTypes = setOf(MediaType.IMAGES, MediaType.VIDEO)
if (hasReadExternalStoragePermission(mediaTypes)) {
println("Read external storage permission already granted for selected media types.")
} else {
requestReadExternalStoragePermission(mediaTypes,
onGranted = {
println("Read external storage permission granted for selected media types.")
},
onDenied = {
println("Read external storage permission denied.")
}
)
}
}
```
### ๐ง Key Points
- Minimal setup: Add the required permissions in the manifest file.
- Comprehensive: Handles a wide range of Android permissions.
- Developer-friendly: Simple APIs with clear callbacks for permission handling.
This library is available on Maven Central. To include it in your project, add the following dependency to your `build.gradle.kts` :
```kotlin
implementation("io.github.kdroidfilter:platformtools.permissionhandler:0.2.7")
```
---
## ๐ License
PlatformTools is licensed under the [MIT License](https://opensource.org/licenses/MIT). Feel free to use, modify, and distribute the library under the terms of the license.
---
## ๐ฅ Contributions
Contributions are welcome! If you want to improve this library, please feel free to submit a pull request or open an issue.
---
## ๐ฃ Demo Application
A demo is available in the `sample` module, showcasing the main features of all the modules included in this library.
Additionally, a demo application with an integrated updater using this library is available [here](https://github.com/kdroidFilter/AppwithAutoUpdater).