diff --git a/docs/README.md b/docs/README.md index f8fda40..fdabb45 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,50 +1,382 @@ -# ion-android-camera +# IONCameraLib -Welcome to **ion-android-camera**. This repository serves as a template to create repositories used to build Android libraries. This file will guide you through that process, that is defined by two sequential steps: +A modern, flexible and feature-rich camera and media library for Android apps. Includes advanced photo, video, and gallery management with easy integration for Kotlin and Android projects. -1. Use the current repository as the template for the new one. -2. Clone the new repository on our machine. -3. Run a script that updates the created repository with the correct information. +## Installation -These steps are detailed in the next sections. +### Gradle -:warning: Every step listed here must be successfully completed before you start working on the new repository. +Add the following to your module's `build.gradle` file: -## Create a Repository Based on the Template +```groovy +dependencies { + implementation 'io.ionic.libs:ioncameralib:1.0.0' +} +``` + +Make sure you have the appropriate Maven repository declared in your project-level `build.gradle` or `settings.gradle`: + +```groovy +repositories { + google() + mavenCentral() +} +``` + +## Usage + +### Basic Camera Operations + +```kotlin +import io.ionic.libs.ioncameralib.manager.IONCAMRCameraManager +import io.ionic.libs.ioncameralib.manager.IONCAMRGalleryManager +import io.ionic.libs.ioncameralib.manager.IONCAMREditManager +import io.ionic.libs.ioncameralib.manager.IONCAMRVideoManager +import io.ionic.libs.ioncameralib.model.IONCAMRCameraParameters +import io.ionic.libs.ioncameralib.model.IONCAMREditParameters +import io.ionic.libs.ioncameralib.model.IONCAMRVideoParameters +import io.ionic.libs.ioncameralib.model.IONCAMRMediaType + +class MainActivity : AppCompatActivity() { + + private lateinit var cameraManager: IONCAMRCameraManager + private lateinit var galleryManager: IONCAMRGalleryManager + private lateinit var editManager: IONCAMREditManager + private lateinit var videoManager: IONCAMRVideoManager + + // Store parameters to use in launcher callbacks + private var cameraParameters: IONCAMRCameraParameters? = null + private var videoParameters: IONCAMRVideoParameters? = null + private var editParameters: IONCAMREditParameters? = null + + // Launchers must be registered at class level (before the activity starts) + private val cameraLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + val params = cameraParameters ?: return@registerForActivityResult + cameraManager.processResultFromCamera( + activity = this, + intent = result.data, + camParameters = params, + onMediaResult = { mediaResult -> println("Photo captured: ${mediaResult.uri}") }, + onError = { error -> println("Error: ${error.description}") } + ) + } + + private val videoLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + val params = videoParameters ?: return@registerForActivityResult + lifecycleScope.launch { + cameraManager.processResultFromVideo( + activity = this@MainActivity, + uri = result.data?.data, + fromGallery = params.saveToGallery, + isPersistent = params.isPersistent, + includeMetadata = params.includeMetadata, + onSuccess = { mediaResult -> println("Video captured: ${mediaResult.uri}") }, + onError = { error -> println("Error: ${error.description}") } + ) + } + } + + private val galleryLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + lifecycleScope.launch { + galleryManager.onChooseFromGalleryResult( + activity = this@MainActivity, + resultCode = result.resultCode, + intent = result.data, + includeMetadata = false, + onSuccess = { results -> println("Selected ${results.size} item(s)") }, + onError = { error -> println("Error: ${error.description}") } + ) + } + } + + private val editLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + val params = editParameters ?: IONCAMREditParameters( + editURI = null, fromUri = false, saveToGallery = false, includeMetadata = false + ) + editManager.processResultFromEdit( + activity = this, + intent = result.data, + editParameters = params, + onImage = { base64 -> println("Edited image (base64): $base64") }, + onMediaResult = { mediaResult -> println("Edited image URI: ${mediaResult.uri}") }, + onError = { error -> println("Error: ${error.description}") } + ) + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setupManagers() + } + + private fun setupManagers() { + val exifHelper = IONCAMRExifHelper() + val fileHelper = IONCAMRFileHelper() + val mediaHelper = IONCAMRMediaHelper() + val imageHelper = IONCAMRImageHelper() + + cameraManager = IONCAMRCameraManager( + applicationId = packageName, + exif = exifHelper, + fileHelper = fileHelper, + mediaHelper = mediaHelper, + imageHelper = imageHelper + ) + galleryManager = IONCAMRGalleryManager( + exif = exifHelper, + fileHelper = fileHelper, + mediaHelper = mediaHelper, + imageHelper = imageHelper + ) + editManager = IONCAMREditManager( + applicationId = packageName, + exif = exifHelper, + fileHelper = fileHelper, + mediaHelper = mediaHelper, + imageHelper = imageHelper + ) + videoManager = IONCAMRVideoManager(fileHelper = fileHelper) + } + + // Take a photo + fun capturePhoto() { + cameraParameters = IONCAMRCameraParameters( + mQuality = 80, + targetWidth = 1024, + targetHeight = 768, + encodingType = 0, // 0 = JPEG, 1 = PNG + mediaType = 0, + allowEdit = true, + correctOrientation = true, + saveToPhotoAlbum = false, + includeMetadata = false + ) + cameraManager.takePhoto( + activity = this, + encodingType = cameraParameters!!.encodingType, + launcher = cameraLauncher + ) + } + + // Record a video + fun recordVideo() { + videoParameters = IONCAMRVideoParameters( + saveToGallery = false, + includeMetadata = false, + isPersistent = true + ) + cameraManager.recordVideo( + activity = this, + saveVideoToGallery = videoParameters!!.saveToGallery, + launcher = videoLauncher, + onError = { error -> println("Error: ${error.description}") } + ) + } + + // Choose from gallery + fun chooseFromGallery() { + galleryManager.chooseFromGallery( + activity = this, + mediaType = IONCAMRMediaType.PICTURE, + allowMultiSelect = false, + limit = 1, + launcher = galleryLauncher + ) + } + + // Edit an image from URI + fun editURIPhoto(filePath: String) { + editParameters = IONCAMREditParameters( + editURI = filePath, + fromUri = true, + saveToGallery = false, + includeMetadata = false + ) + editManager.editURIPicture( + activity = this, + pictureFilePath = filePath, + launcher = editLauncher, + onError = { error -> println("Error: ${error.description}") } + ) + } -First, we need to create a new repository. To accomplish this, please press the **Use this template** button available on the repository's GitHub webpage. + // Edit an image from Base64 string + fun editBase64Image(base64Image: String) { + editParameters = null + editManager.editImage( + activity = this, + image = base64Image, + launcher = editLauncher + ) + } -![Use this template button](./assets/useThisTemplateButton.png) + // Play a video + fun playVideo(videoUri: String) { + videoManager.playVideo( + activity = this, + videoUri = videoUri, + onSuccess = { println("Video playback started") }, + onError = { error -> println("Video playback error: ${error.description}") } + ) + } -Next, we have to define the new repository's name. In order to get the maximum performance of the following step, we advise you to use the **[ProjectName]Lib-Android** format for the name. The names used for the **Health and Fitness** and the **Social Logins** are valid examples of the expected format (_OSHealthFitnessLib-Android_ and _OSSocialLoginsLib-Android_ respectively). + // Clean temporary video files + fun cleanupTemporaryFiles() { + cameraManager.deleteVideoFilesFromCache(this) + } -The following image shows an example of the creation of a repository for the Android' Payments Library. + override fun onDestroy() { + super.onDestroy() + cameraManager.onDestroy(this) + } +} +``` + +### Advanced Usage Examples + +```kotlin +// Multiple selection from gallery (video or photo) +fun selectMultipleMedia() { + galleryManager.chooseFromGallery( + activity = this, + mediaType = IONCAMRMediaType.ALL, + allowMultiSelect = true, + limit = 10, + launcher = galleryLauncher + ) +} + +// High quality photo with metadata +fun takeHighQualityPhoto() { + cameraParameters = IONCAMRCameraParameters( + mQuality = 100, + targetWidth = 2048, + targetHeight = 1536, + encodingType = 0, // JPEG + mediaType = 0, + allowEdit = false, + correctOrientation = true, + saveToPhotoAlbum = true, + includeMetadata = true + ) + cameraManager.takePhoto( + activity = this, + encodingType = cameraParameters!!.encodingType, + launcher = cameraLauncher + ) +} + +// Video recording saved to gallery +fun recordVideoToGallery() { + videoParameters = IONCAMRVideoParameters( + saveToGallery = true, + includeMetadata = false, + isPersistent = true + ) + cameraManager.recordVideo( + activity = this, + saveVideoToGallery = videoParameters!!.saveToGallery, + launcher = videoLauncher, + onError = { error -> println("Error: ${error.description}") } + ) +} +``` -![Example for payments repository name](./assets/repositoryNameExample.png) +## Key Components -After filling up the form as needed, the last step to effectively create the repository is the click on the **Create repository from template** button. +### Manager Classes -![Create repository from template button](./assets/createRepositoryButton.png) +- **`IONCAMRCameraManager`**: Handles photo capture and video recording via the device camera +- **`IONCAMRGalleryManager`**: Handles media selection from the device gallery +- **`IONCAMREditManager`**: Handles photo editing and cropping +- **`IONCAMRVideoManager`**: Handles video playback -## Clone the New Repository +### Configuration Parameters -After completing the previous step, the next one is something common done in every repository a developer needs to do work on: clone the repository on the local machine. +- **`IONCAMRCameraParameters`**: Configures photo capture settings (quality, dimensions, encoding type, media type, edit, orientation correction, album saving, metadata) +- **`IONCAMRVideoParameters`**: Configures video recording settings (save to gallery, include metadata, persistence) +- **`IONCAMREditParameters`**: Configures image editing settings (source URI, save to gallery, metadata) -## Run the **generator_script.sh** +### Result Model -To finish the process, we just have one last thing to do. Run the **generator_script.sh** script that automates a couple of changes we need to apply. It is included in the _scripts_ folder. +All successful media operations return `IONCAMRMediaResult` objects: -To run the script, please execute the following commands on **Terminal**: +```kotlin +data class IONCAMRMediaResult( + val type: Int, // Media type (image or video) + val uri: String, // File URI of the captured or selected media item + val thumbnail: String?, // Base64 thumbnail (if available) + val metadata: IONCAMRMediaMetadata?, // File metadata (if requested) + val saved: Boolean // Whether the file was saved to the gallery +) +data class IONCAMRMediaMetadata( + val size: Long?, // File size in bytes + val duration: Int?, // Duration in seconds (video only) + val format: String?, // File format (e.g. "jpeg", "mp4") + val resolution: String?, // Image/video resolution (e.g. "1920x1080") + val creationDate: String? // File creation date +) ``` -cd scripts -sh generator_script.sh + +### Error Handling + +All manager methods expose an `onError` callback that receives an `IONCAMRError`. Each error carries a numeric `code` and a human-readable `description`. Errors are grouped into categories: permission errors, user cancellations, file errors, and general processing errors. See `IONCAMRError` for the full list. + +```kotlin +onError = { error -> + // error.code — numeric error code (e.g. for logging) + // error.description — human-readable message + showErrorAlert("Error ${error.code}: ${error.description}") +} ``` -Here's the complete list of what the script does: +## Permissions + +Add the following permissions to your app's `AndroidManifest.xml`: + +```xml + + + + + + + +``` + +You must also request camera and storage permissions at runtime before invoking camera or gallery operations. Use Android's standard permission request flow or a library such as [ActivityResultContracts.RequestPermission](https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts.RequestPermission). + +## Requirements + +- Android API 24+ +- Android Gradle Plugin 8.7.3+ +- Kotlin 1.9.24+ +- Java 17+ + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +## Contributing + +1. Fork the project +2. Create your feature branch (`git checkout -b feature/AmazingFeature`) +3. Commit your changes (`git commit -m 'Add some AmazingFeature'`) +4. Push to the branch (`git push origin feature/AmazingFeature`) +5. Open a Pull Request + +## Support -- The script provides a bit of information, such as mentioning the name that it will use as the Library name (its based on the one you used while creating the repository on GitHub). -- Requests the user for the application's package identifier. The format required is provided and needs to be complied with in order to advance. -- It informs that the script itself will be deleted, as it is a one time execution only. -- It performs the needed changes, replacing all placeholder's organisational identifier and library name for the ones provided by the user. -- To conclude, the script commits and pushes the changes to the remote repository. +- Report issues on our [Issue Tracker](https://github.com/ionic-team/ion-android-camera/issues) diff --git a/src/main/kotlin/io/ionic/libs/ioncameralib/model/IONCAMRVideoParameters.kt b/src/main/kotlin/io/ionic/libs/ioncameralib/model/IONCAMRVideoParameters.kt index 119a6ab..6fe39fb 100644 --- a/src/main/kotlin/io/ionic/libs/ioncameralib/model/IONCAMRVideoParameters.kt +++ b/src/main/kotlin/io/ionic/libs/ioncameralib/model/IONCAMRVideoParameters.kt @@ -4,5 +4,6 @@ import com.google.gson.annotations.SerializedName data class IONCAMRVideoParameters( @SerializedName("saveToGallery") val saveToGallery: Boolean, - @SerializedName("includeMetadata") val includeMetadata: Boolean + @SerializedName("includeMetadata") val includeMetadata: Boolean, + @SerializedName("isPersistent") val isPersistent: Boolean = true )