{"id":13611059,"url":"https://github.com/onseok/peekaboo","last_synced_at":"2025-04-13T01:34:27.706Z","repository":{"id":211397274,"uuid":"721608337","full_name":"onseok/peekaboo","owner":"onseok","description":"🌄 Kotlin Multiplatform library for Compose Multiplatform, designed for seamless integration of an image picker feature in iOS and Android applications. ","archived":false,"fork":false,"pushed_at":"2024-03-26T13:54:05.000Z","size":377,"stargazers_count":92,"open_issues_count":7,"forks_count":12,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-12T17:07:19.142Z","etag":null,"topics":["android","compose-multiplatform","image-picker","image-picker-android","ios","kmp-camera","kmp-image","kotlin","kotlin-multiplatform","kotlin-multiplatform-camera"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/onseok.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-11-21T12:12:36.000Z","updated_at":"2024-04-15T15:58:21.927Z","dependencies_parsed_at":"2023-12-15T21:15:26.713Z","dependency_job_id":"d6ca2ac4-84a5-4c40-905e-5afb88f3e0f1","html_url":"https://github.com/onseok/peekaboo","commit_stats":null,"previous_names":["team-preat/peekaboo","onseok/peekaboo"],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onseok%2Fpeekaboo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onseok%2Fpeekaboo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onseok%2Fpeekaboo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/onseok%2Fpeekaboo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/onseok","download_url":"https://codeload.github.com/onseok/peekaboo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223558517,"owners_count":17165142,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","compose-multiplatform","image-picker","image-picker-android","ios","kmp-camera","kmp-image","kotlin","kotlin-multiplatform","kotlin-multiplatform-camera"],"created_at":"2024-08-01T19:01:51.326Z","updated_at":"2024-11-07T17:31:17.489Z","avatar_url":"https://github.com/onseok.png","language":"Kotlin","funding_links":[],"categories":["Uncategorized","Kotlin"],"sub_categories":["Uncategorized"],"readme":"# peekaboo\n[![Kotlin](https://img.shields.io/badge/kotlin-1.9.23-blue.svg?logo=kotlin)](http://kotlinlang.org)\n[![Compose Multiplatform](https://img.shields.io/badge/Compose%20Multiplatform-v1.6.2-blue)](https://github.com/JetBrains/compose-multiplatform)\n[![Maven Central](https://img.shields.io/maven-central/v/io.github.onseok/peekaboo-image-picker?color=orange)](https://search.maven.org/search?q=g:io.github.onseok)\n[![Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0)\n\n[![Build](https://github.com/onseok/peekaboo/actions/workflows/ci_check.yml/badge.svg)](https://github.com/onseok/peekaboo/actions/workflows/ci_check.yml)\n![badge-android](http://img.shields.io/badge/platform-android-6EDB8D.svg?style=flat)\n![badge-ios](http://img.shields.io/badge/platform-ios-CDCDCD.svg?style=flat)\n[![Featured in androidweekly.net](https://img.shields.io/badge/Featured%20in%20androidweekly.net-Issue%20%23601-4998C2)](https://androidweekly.net/issues/issue-601)\n\u003ca href=\"https://mailchi.mp/kotlinweekly/kotlin-weekly-385\"\u003e\u003cimg alt=\"Kotlin Weekly\" src=\"https://img.shields.io/badge/Kotlin_Weekly-%23385-purple\"/\u003e\u003c/a\u003e\n\n📂 Kotlin Multiplatform library for Compose Multiplatform, designed for seamless integration of an image picker feature in iOS and Android applications.\n\n## Getting started\n\n### Compose Multiplatform\n\n`peekaboo` is based on `Compose Multiplatform`, currently targeting only `iOS` and `Android`. \u003cbr/\u003e\nPlease note that it primarily focuses on these platforms, and additional platforms may be considered in the future. \u003cbr/\u003e\nWhen using `peekaboo` on Android, ensure that Google's Jetpack Compose version is compatible with `peekaboo`'s Compose Multiplatform version. \u003cbr/\u003e\n\n## Installation\n\nThe minimum supported Android SDK is 24 (Android 7.0).\n\nIn your `commonMain` configuration, add the desired dependency, either **`peekaboo-ui`** or **`peekaboo-image-picker`**, to your project. Both are available on Maven Central.\n\u003cbr/\u003e\n### Without Version Catalog\n\n```kotlin\ncommonMain {\n    dependencies {\n        // peekaboo-ui\n        implementation(\"io.github.onseok:peekaboo-ui:$latest_version\")\n\n        // peekaboo-image-picker\n        implementation(\"io.github.onseok:peekaboo-image-picker:$latest_version\")\n    }\n}\n```\n\n\n### With Version Catalog\n\nFirst, define the version in `libs.versions.toml`:\n\n```toml\n[versions]\npeekaboo = \"0.5.2\"\n\n[libraries]\npeekaboo-ui = { module = \"io.github.onseok:peekaboo-ui\", version.ref = \"peekaboo\" }\npeekaboo-image-picker = { module = \"io.github.onseok:peekaboo-image-picker\", version.ref = \"peekaboo\" }\n```\n\nThen, in your `commonMain` configuration, reference the defined version:\n\n```kotlin\ncommonMain {\n    dependencies {\n        // peekaboo-ui\n        implementation(libs.peekaboo.ui)\n\n        // peekaboo-image-picker\n        implementation(libs.peekaboo.image.picker)\n    }\n}\n```\n\n### Artifacts\n\n| Name                    | Description                                                                 |\n|-------------------------|-----------------------------------------------------------------------------|\n| `peekaboo-ui` |Provides user-friendly UI elements, including a custom camera view for easy image capture, suitable for both `iOS` and `Android` platforms. |\n| `peekaboo-image-picker` | Simplifies the process of selecting single or multiple images both in `iOS` and `Android` platforms. |\n\n\u003cbr/\u003e\n\n## Usage\n### Xcode setup\nIn order to access the camera on iOS devices, it's essential to include a specific key-value pair in the `Info.plist` file of your iOS project. This key-value pair comprises a key that identifies the type of permission being requested and a value that provides a user-friendly description explaining why the app needs access to the camera.\n\nHere's the key-value pair you should add to your `Info.plist`:\n```xml\n\u003ckey\u003ePrivacy - Camera Usage Description\u003c/key\u003e\n\u003cstring\u003eThis app uses camera for capturing photos.\u003c/string\u003e\n```\n\n### Custimizable Camera UI\n`PeekabooCamera` is a `composable` function that provides a customizable camera UI within a `Compose Multiplatform` application.\n\n### Simple Camera UI\n\n```kotlin\n@Composable\nfun CustomCameraView() {\n    val state = rememberPeekabooCameraState(onCapture = { /* Handle captured images */ })\n    PeekabooCamera(\n        state = state,\n        modifier = Modifier.fillMaxSize(),\n        permissionDeniedContent = {\n            // Custom UI content for permission denied scenario\n        },\n    )\n}\n```\n\n### Camera UI with overlay\n\n```kotlin\n@Composable\nfun CustomCameraView() {\n    val state = rememberPeekabooCameraState(onCapture = { /* Handle captured images */ })\n    Box(modifier = Modifier.fillMaxSize()) {\n        PeekabooCamera(\n            state = state,\n            modifier = Modifier.fillMaxSize(),\n            permissionDeniedContent = {\n                // Custom UI content for permission denied scenario\n            },\n        )\n        // Draw here UI you need with provided state\n        YourCameraOverlay(\n            state = state,\n            modifier = Modifier.fillMaxSize(),\n        )\n    }\n}\n```\n- **`state`** : The `PeekabooCameraState` to control camera.\n- **`permissionDeniedContent`** : An optional `composable` lambda that provides content to be displayed when camera permission is denied. This allows users to define a custom UI to inform or guide the user when camera access has been denied. The content can be informative text, an image, a button to redirect the user to settings, or any other `composable` content. This lambda will be invoked within the `PeekabooCamera` composable scope, replacing the camera preview with the user-defined UI.\n\n### Camera state\n\n```kotlin\nrememberPeekabooCameraState(\n    initialCameraMode: CameraMode = CameraMode.Back,\n    onCapture: (ByteArray?) -\u003e Unit,\n)\n```\n- **`initialCameraMode`** : The initial camera mode (front or back). Default is [CameraMode.Back]. Changes does not affect state. To toggle use [PeekabooCameraState.toggleCamera]\n- **`onCapture`** : A lambda called when a photo is captured, providing the photo as a ByteArray or null if the capture fails.\n- **`PeekabooCameraState.isCameraReady`** : True if camera already available for show\n- **`PeekabooCameraState.isCapturing`** : True if camera is in progress of capture\n- **`PeekabooCameraState.cameraMode`** : Current camera mode (front or back)\n\n\n#### Capturing an Image from Camera\n| Android                                                         | iOS                                                     |\n|-----------------------------------------------------------------|---------------------------------------------------------|\n| \u003cimg src=\"https://github.com/onseok/onseok/assets/76798309/897a0104-2e8d-4339-90fb-2f61807aa56d\" width=\"300\" height=\"700\"\u003e | \u003cimg src=\"https://github.com/onseok/onseok/assets/76798309/fe414cc2-370a-4b0d-9558-c60e1fbbb4f7\" width=\"300\" height=\"700\"\u003e |\n\n#### Toggling Camera Mode Between Front and Back\n| Android                                                         | iOS                                                     |\n|-----------------------------------------------------------------|---------------------------------------------------------|\n| \u003cimg src=\"https://github.com/onseok/onseok/assets/76798309/477f49f8-389d-4ba6-a2d1-60155cab355e\" width=\"300\" height=\"700\"\u003e | \u003cimg src=\"https://github.com/onseok/onseok/assets/76798309/022da284-cf58-4ce0-9fc1-e592885f09b9\" width=\"300\" height=\"700\"\u003e |\n\n#### Handling Denied Camera Permissions\n| Android                                                         | iOS                                                     |\n|-----------------------------------------------------------------|---------------------------------------------------------|\n| \u003cimg src=\"https://github.com/onseok/onseok/assets/76798309/61511ec6-45b9-48bd-9267-fe9f2a086008\" width=\"300\" height=\"700\"\u003e | \u003cimg src=\"https://github.com/onseok/onseok/assets/76798309/604ed9d9-d6e0-47ac-9393-5cb627a4f13d\" width=\"300\" height=\"700\"\u003e |\n\n\n### Select Single Image\n\n```kotlin\nval scope = rememberCoroutineScope()\n\nval singleImagePicker = rememberImagePickerLauncher(\n    selectionMode = SelectionMode.Single,\n    scope = scope,\n    onResult = { byteArrays -\u003e\n        byteArrays.firstOrNull()?.let {\n            // Process the selected images' ByteArrays.\n            println(it)\n        }\n    }\n)\n\nButton(\n    onClick = {\n        singleImagePicker.launch()\n    }\n) {\n    Text(\"Pick Single Image\")\n}\n```\n\n| Android                                                         | iOS                                                     |\n|-----------------------------------------------------------------|---------------------------------------------------------|\n| \u003cimg src=\"https://github.com/TEAM-PREAT/peekaboo/assets/76798309/03346992-cf2a-4424-88e5-fa53afd36eac\" width=\"300\" height=\"700\"\u003e | \u003cimg src=\"https://github.com/TEAM-PREAT/peekaboo/assets/76798309/7b562f6f-e5d5-4858-85b8-acf7196d646f\" width=\"300\" height=\"700\"\u003e |\n\nSimply select the desired image with an intuitive interface.\n\n\u003cbr/\u003e\n\n### Select Multiple Images\n\nIf you want to select multiple images, you can use `SelectionMode.Multiple()`. And you can set the maximum number of images to select.\nIf you didn't set max selection, the default value is maximum number that the system supports.\n\n```kotlin\nval scope = rememberCoroutineScope()\n\nval multipleImagePicker = rememberImagePickerLauncher(\n    // Optional: Set a maximum selection limit, e.g., SelectionMode.Multiple(maxSelection = 5).\n    // Default: No limit, depends on system's maximum capacity.\n    selectionMode = SelectionMode.Multiple(maxSelection = 5),\n    scope = scope,\n    onResult = { byteArrays -\u003e\n        byteArrays.forEach {\n            // Process the selected images' ByteArrays.\n            println(it)\n        }\n    }\n)\n\nButton(\n    onClick = {\n        multipleImagePicker.launch()\n    }\n) {\n    Text(\"Pick Multiple Images\")\n}\n```\n\n| Android                                                         | iOS                                                     |\n|-----------------------------------------------------------------|---------------------------------------------------------|\n| \u003cimg src=\"https://github.com/TEAM-PREAT/peekaboo/assets/76798309/e26ae1b3-4333-41a9-92c3-ebe56c337d79\" width=\"300\" height=\"700\"\u003e | \u003cimg src=\"https://github.com/TEAM-PREAT/peekaboo/assets/76798309/a990ce09-d485-4a0f-9416-8af8b040cf2d\" width=\"300\" height=\"700\"\u003e |\n\n\u003cbr/\u003e\n\n## Image Resizing Options\n`peekaboo` offers customizable resizing options for both single and multiple image selections, along with a new feature to resize images only if they exceed a certain file size. \u003cbr/\u003e\nThis feature allows you to resize the selected images to specific dimensions, optimizing them for your application's requirements and enhancing performance.\n\n- The default resizing dimensions are set to `800 x 800` pixels.\n- The default threshold for resizing is set to `1MB`, meaning images larger than this size will be resized.\n- You can customize the resizing **dimensions**, **threshold**, and now the **compression quality** according to your needs.\n\n### Usage\nSet the `resizeOptions` parameter in `rememberImagePickerLauncher` with your desired dimensions, threshold and compression quality:\n\n```kotlin\nval resizeOptions = ResizeOptions(\n    width = 1200, // Custom width\n    height = 1200, // Custom height\n    resizeThresholdBytes = 2 * 1024 * 1024L, // Custom threshold for 2MB,\n    compressionQuality = 0.5 // Adjust compression quality (0.0 to 1.0)\n)\n```\n\n#### Single Image Selection with Resizing\n```kotlin\nval singleImagePicker = rememberImagePickerLauncher(\n    selectionMode = SelectionMode.Single,\n    scope = rememberCoroutineScope(),\n    resizeOptions = resizeOptions,\n    onResult = { byteArrays -\u003e\n        byteArrays.firstOrNull()?.let {\n            // Process the resized image's ByteArray\n            println(it)\n        }\n    }\n)\n```\n\n#### Multiple Images Selection with Resizing\n```kotlin\nval multipleImagePicker = rememberImagePickerLauncher(\n    selectionMode = SelectionMode.Multiple(maxSelection = 5),\n    scope = rememberCoroutineScope(),\n    resizeOptions = resizeOptions,\n    onResult = { byteArrays -\u003e\n        byteArrays.forEach {\n            // Process the resized images' ByteArrays\n            println(it)\n        }\n    }\n)\n```\n\n\u003e💡 Note: While resizing, the aspect ratio of the original images is preserved. The final dimensions may slightly vary to maintain the original proportions.\n\n\u003cbr/\u003e\n\n## Image Filter Options\n**`peekaboo-image-picker`** now offers customizable filter options for selected images.\n\nThis feature is available on both `Android` and `iOS` devices.\n\n| Default                                                         | GrayScale                                                     | Sepia                                                     | Invert                                                     |\n|-----------------------------------------------------------------|---------------------------------------------------------|---------------------------------------------------------|---------------------------------------------------------|\n| \u003cimg src=\"https://github.com/TEAM-PREAT/peekaboo/assets/76798309/2f47becf-1512-47e2-83c1-2120d58d6d11\" width=\"300\" height=\"700\"\u003e | \u003cimg src=\"https://github.com/TEAM-PREAT/peekaboo/assets/76798309/3c864a79-83df-451c-91e1-1e71fbdb3066\" width=\"300\" height=\"700\"\u003e | \u003cimg src=\"https://github.com/TEAM-PREAT/peekaboo/assets/76798309/52dee8e8-1979-4725-bfca-12af1b3c4b3d\" width=\"300\" height=\"700\"\u003e | \u003cimg src=\"https://github.com/TEAM-PREAT/peekaboo/assets/76798309/cbe927d1-55c2-40c5-ae35-2be81997e9fb\" width=\"300\" height=\"700\"\u003e |\n\n### Usage\nSet the `filterOptions` parameter in `rememberImagePickerLauncher`:\n\n```kotlin\nval imagePicker = rememberImagePickerLauncher(\n    selectionMode = SelectionMode.Single,\n    scope = rememberCoroutineScope(),\n    filterOptions = FilterOptions.GrayScale,\n    onResult = { byteArrays -\u003e\n        // Process the filtered images' ByteArrays\n    }\n)\n```\n\n\u003e💡 Note: The default filter option is `Default`, which applies no filter.\n\u003e Choose from `GrayScale`, `Sepia`, or `Invert` for different effects.\n\n\u003cbr/\u003e\n\n## ByteArray to ImageBitmap Conversion\nWe've added a new extension function `toImageBitmap()` to convert a `ByteArray` into an `ImageBitmap`. \u003cbr/\u003e\nThis function simplifies the process of converting image data into a displayable format, enhancing the app's capability to handle image processing efficiently.\n\n### Usage\n```kotlin\nval imageBitmap = byteArray.toImageBitmap()\n```\n\n\u003cbr/\u003e\n\n## Contributions 🙏\n\nContributions are always welcome!\n\nIf you'd like to contribute, please feel free to create a PR or open an issue. 👍\n\n\u003cbr/\u003e\n\n## Stargazers :star:\nSupport it by joining __[stargazers](https://github.com/onseok/peekaboo/stargazers)__ for this repository. :star: \u003cbr\u003e\n\n## License\n\n```\nCopyright 2024 onseok\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   https://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonseok%2Fpeekaboo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fonseok%2Fpeekaboo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonseok%2Fpeekaboo/lists"}