Skip to content

Commit

Permalink
Merge pull request #5 from Kashif-Mehmood-KM/patch-2
Browse files Browse the repository at this point in the history
Update README.MD
  • Loading branch information
Kashif-E authored Sep 27, 2024
2 parents 22482f9 + 9d8b370 commit c003999
Showing 1 changed file with 199 additions and 66 deletions.
265 changes: 199 additions & 66 deletions README.MD
Original file line number Diff line number Diff line change
@@ -1,116 +1,249 @@
# CameraK App
# CameraK Library

### The Library is very much WIP and is being redesigned in a seperate branch you can play around with it but dont use it in a critical project.
[![Version](https://img.shields.io/github/v/release/kashif-e/camerak)](https://github.com/Kashif-E/CameraK/releases/tag/0.0.5)
<p align="center">
<a href="https://github.com/Kashif-E/CameraK/releases/tag/0.0.5">
<img src="https://img.shields.io/github/v/release/kashif-e/camerak" alt="Version">
</a>
<a href="https://mailchi.mp/kotlinweekly/kotlin-weekly-425">
<img src="https://img.shields.io/badge/Kotlin_Weekly-425-blue" alt="Kotlin Weekly #425">
</a>
</p>

#### Want to support my work?
<a href="https://www.buymeacoffee.com/kashifmehmood"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=kashifmehmood&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff" /></a>
## Overview

The **CameraK Library** is a camera solution designed for Compose Multiplatform, currently
supporting both **Android** and **iOS**. While there are plans to expand support to additional
platforms, this will take time.

## Overview
CameraK offers features such as **Camera Preview**, **Image Capture**, **Saving Images Locally**,
and **Exposing Images as ByteArrays**. It has a plugin-based API that allows developers to extend
and enhance its functionality. Currently, two plugins are available: one for saving images and
another for scanning QR codes.

The CameraK App is a sample application demonstrating the use of the CameraX library in a Jetpack Compose environment. It provides functionalities to capture images, toggle flash mode, and switch between front and back cameras.
## Installation

## Features
```Kotlin
implementation("io.github.kashif-mehmood-km:camerak:+")
```

- **Camera Preview**: Displays a live camera feed using `CameraKPreview`.
- **Capture Image**: Allows users to capture images in JPEG format.
- **Toggle Flash**: Users can toggle the flash mode (on/off).
- **Switch Camera**: Users can switch between the front and back cameras.
- **Save Captured Images**: Captured images are saved to the device's storage.
if you want to save images to device add the plugin:

```Kotlin
implementation("io.github.kashif-mehmood-km:image_saver_plugin:0.0.1")
````

## Installation
if you want to add QR scanning capability then you need the QR scanner plugin:

```Kotlin
implementation("io.github.kashif-mehmood-km:camerak:+")
implementation("io.github.kashif-mehmood-km:qr_scanner_plugin:0.0.1")
```

## Supported Platforms

- Android
- Android
- IOS
- More will be added later

## Usage

This is a simple example of how to use the CameraK library in your app.

### Permissions
#### Permissions

Add the following permissions to your `AndroidManifest.xml` file:

```xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
```
```xml

<uses-permission android:name="android.permission.CAMERA" /><uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
```

Add the following to your info.plist file for iOS
```xml
<key>NSCameraUsageDescription</key>
<string>Camera permission is required for the app to work.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Photo Library permission is required for the app to work.</string>

```xml

<key>NSCameraUsageDescription</key><string>Camera permission is required for the app to work.
</string><key>NSPhotoLibraryUsageDescription</key><string>Photo Library permission is required for
the app to work.
</string>
```

Checking for permission and asking if needed

```Kotlin
// Initialize Camera Permission State based on current permission status
val cameraPermissionState = remember {
mutableStateOf(
permissions.hasCameraPermission()
)
}

// Initialize Storage Permission State
val storagePermissionState = remember {
mutableStateOf(
permissions.hasStoragePermission()
)
}

if (!cameraPermissionState.value) {
permissions.RequestStoragePermission(onGranted = { cameraPermissionState.value = true },
onDenied = {
println("Camera Permission Denied")
})
}


if (!storagePermissionState.value) {
permissions.RequestStoragePermission(onGranted = { storagePermissionState.value = true },
onDenied = {
println("Storage Permission Denied")
})
}

// Initialize CameraController only when permissions are granted
if (cameraPermissionState.value && storagePermissionState.value) {
}

```

If permissions are granted we first create a camera controller

```Kotlin
val cameraController = remember { mutableStateOf<CameraController?>(null) }
```

### Compose Implementation
After this if needed, create plugins

```Kotlin
val imageSaverPlugin = createImageSaverPlugin(
config = ImageSaverConfig(
isAutoSave = false, // Set to true to enable automatic saving
prefix = "MyApp", // Prefix for image names when auto-saving
directory = Directory.PICTURES, // Directory to save images
customFolderName = "CustomFolder" // Custom folder name within the directory, only works on android for now
)
)
val qrScannerPlugin = createQRScannerPlugin {
println("QR Code Scanned: $it")
}
```

After this we can create a Camera Preview and pass camera configuration, the it will create a
`CameraController` and we can get it from the `onCameraControllerReady` callback. Once we get the
controller we can then show the camera screen.

```Kotlin
CameraPreview(modifier = Modifier.fillMaxSize(), cameraConfiguration = {
setCameraLens(CameraLens.BACK)
setFlashMode(FlashMode.OFF)
setImageFormat(ImageFormat.JPEG)
setDirectory(Directory.PICTURES)
addPlugin(imageSaverPlugin)
addPlugin(qrScannerPlugin)
}, onCameraControllerReady = {
cameraController.value = it
println("Camera Controller Ready ${cameraController.value}")
qrScannerPlugin.startScanning()
})
cameraController.value?.let { controller ->
CameraScreen(cameraController = controller, imageSaverPlugin)
}
```

Here is a sample camera screen that is in the `Sample` module

```Kotlin
@OptIn(ExperimentalResourceApi::class, ExperimentalUuidApi::class)
@Composable
private fun CameraK(
controller: CameraController,
scope: CoroutineScope,
) {
var imageBitmap: ImageBitmap? by remember { mutableStateOf(null) }

val flashMode = remember(controller.getFlashMode()) {
controller.getFlashMode() == FlashMode.ON
}
Box(modifier = Modifier.fillMaxSize()) {
fun CameraScreen(cameraController: CameraController, imageSaverPlugin: ImageSaverPlugin) {
val scope = rememberCoroutineScope()
var imageBitmap by remember { mutableStateOf<ImageBitmap?>(null) }
var isFlashOn by remember { mutableStateOf(false) }

Box(
modifier = Modifier.fillMaxSize()
) {

CameraKPreview(
modifier = Modifier.fillMaxSize(),
cameraController = controller
)
Row(
modifier = Modifier.fillMaxWidth().align(Alignment.TopStart),
horizontalArrangement = Arrangement.SpaceEvenly
modifier = Modifier.fillMaxWidth().padding(16.dp).align(Alignment.TopStart),
horizontalArrangement = Arrangement.SpaceBetween
) {
// Flash Mode Switch
Row(verticalAlignment = Alignment.CenterVertically) {
Text(text = "Flash")
Spacer(modifier = Modifier.width(8.dp))
Switch(checked = isFlashOn, onCheckedChange = {
isFlashOn = it
cameraController.toggleFlashMode()
})
}

Switch(
checked = flashMode,
onCheckedChange = { controller.toggleFlashMode() }
)

Button(onClick = { controller.toggleCameraLens() }) {
Text("Toggle Camera Lens")
// Camera Lens Toggle Button
Button(onClick = { cameraController.toggleCameraLens() }) {
Text(text = "Toggle Lens")
}
}
// Capture Button at the Bottom Center
Button(
onClick = {
scope.launch {
when (val result = cameraController.takePicture()) {
is ImageCaptureResult.Success -> {

imageBitmap = result.byteArray.decodeToImageBitmap()
// If auto-save is disabled, manually save the image
if (!imageSaverPlugin.config.isAutoSave) {
// Generate a custom name or use default
val customName = "Manual_${Uuid.random().toHexString()}"

imageSaverPlugin.saveImage(
byteArray = result.byteArray, imageName = customName
)
}
}

is ImageCaptureResult.Error -> {
println("Image Capture Error: ${result.exception.message}")
}
}
}
}, modifier = Modifier.size(70.dp).clip(CircleShape).align(Alignment.BottomCenter)

Button(onClick = {
scope.launch {
when (val result = controller.takePicture(ImageFormat.PNG)) {
is ImageCaptureResult.Success -> {
imageBitmap = result.image.decodeToImageBitmap()
) {
Text(text = "Capture")
}

controller.savePicture("image.png", result.image, Directory.PICTURES)
}
// Display the captured image
imageBitmap?.let { bitmap ->
Image(
bitmap = bitmap,
contentDescription = "Captured Image",
modifier = Modifier.fillMaxSize().padding(16.dp)
)

is ImageCaptureResult.Error -> {
println(result.exception.message ?: "Error")
}
}
LaunchedEffect(bitmap) {
delay(3000)
imageBitmap = null
}

}, modifier = Modifier.clip(CircleShape).align(Alignment.BottomCenter).padding(16.dp)) {
Text("Capture")
}
}
}
```

You can check the `Sample` for more details.

The library is in an experimental stage, APIs can change/break.

**Contributions**

We welcome contributions! Before submitting a pull request, please open an issue so we can discuss
the proposed changes and collaborate on improving the project.

**Feature Requests**
Feature requests are encouraged, and I’ll do my best to address them as quickly as possible.

## License

```
MIT License
```
MIT License
```

0 comments on commit c003999

Please sign in to comment.