From 6b721669667555ae5931ab60ba06ad1bf51b0b19 Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Mon, 23 Jun 2025 13:19:12 +0530 Subject: [PATCH 01/16] docs: add migration guide for FilePicker library to outline key changes and transition steps --- README.md | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/README.md b/README.md index a4f6456..5d91f5f 100644 --- a/README.md +++ b/README.md @@ -213,6 +213,47 @@ val pickerData = PickerData( ) ) ``` +# Migration Guide: FilePicker Library + +## Overview + +This guide outlines the changes from the old code to the new `FilePicker` implementation, focusing on the transition from the Builder pattern to `ActivityResultContract`. + +## Key Changes + +1. **Package and Class Changes**: + The package has changed from `com.nareshchocha.filepickerlibrary.ui` to `com.nareshchocha.filepickerlibrary`. + +2. **Removal of `Builder` Class**: + The `Builder` class is no longer needed. The new code utilizes `ActivityResultContract` for handling file picker actions. + +3. **Introduction of `ActivityResultContracts`**: + File picker operations are now handled by specific `ActivityResultContract` classes, such as `ImageCapture`, `VideoCapture`, and `PickMedia`. + +4. **Logging Support**: + A new `isLoggingEnabled` flag allows enabling logging in the contracts for debugging. + +## Migration Steps + +### 1. **Remove Builder Pattern** + +The `Builder` class is no longer needed. You should transition to using `ActivityResultContracts` instead. + +### 2. **Use `ActivityResultContracts`** + +You can now handle file picker actions with specific contracts. For example: + +- **Old Code**: + ```kotlin + fun imageCaptureBuild(mImageCaptureConfig: ImageCaptureConfig?): Intent = + ImageCaptureActivity.getInstance(context, mImageCaptureConfig) + ``` +- **New Code**: + ```kotlin + val imageCaptureResult = registerForActivityResult(FilePickerResultContracts.ImageCapture()) { result -> + // Handle result + } + ``` --- From 8d78abf8782082f2e8d87c9904a61cfb3d2a1ae3 Mon Sep 17 00:00:00 2001 From: NareshChocha Date: Tue, 24 Jun 2025 21:36:50 +0530 Subject: [PATCH 02/16] docs: add Privacy Policy document for the application --- PrivacyPolicy.md | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 PrivacyPolicy.md diff --git a/PrivacyPolicy.md b/PrivacyPolicy.md new file mode 100644 index 0000000..6fb16a7 --- /dev/null +++ b/PrivacyPolicy.md @@ -0,0 +1,68 @@ +**Privacy Policy** + +This privacy policy applies to the File Picker app (hereby referred to as "Application") for mobile devices that was created by Naresh Chocha (hereby referred to as "Service Provider") as an Open Source service. This service is intended for use "AS IS". + +**Information Collection and Use** + +The Application collects information when you download and use it. This information may include information such as + +* Your device's Internet Protocol address (e.g. IP address) +* The pages of the Application that you visit, the time and date of your visit, the time spent on those pages +* The time spent on the Application +* The operating system you use on your mobile device + +The Application does not gather precise information about the location of your mobile device. + +The Application collects your device's location, which helps the Service Provider determine your approximate geographical location and make use of in below ways: + +* Geolocation Services: The Service Provider utilizes location data to provide features such as personalized content, relevant recommendations, and location-based services. +* Analytics and Improvements: Aggregated and anonymized location data helps the Service Provider to analyze user behavior, identify trends, and improve the overall performance and functionality of the Application. +* Third-Party Services: Periodically, the Service Provider may transmit anonymized location data to external services. These services assist them in enhancing the Application and optimizing their offerings. + +The Service Provider may use the information you provided to contact you from time to time to provide you with important information, required notices and marketing promotions. + +For a better experience, while using the Application, the Service Provider may require you to provide us with certain personally identifiable information. The information that the Service Provider request will be retained by them and used as described in this privacy policy. + +**Third Party Access** + +Only aggregated, anonymized data is periodically transmitted to external services to aid the Service Provider in improving the Application and their service. The Service Provider may share your information with third parties in the ways that are described in this privacy statement. + +The Service Provider may disclose User Provided and Automatically Collected Information: + +* as required by law, such as to comply with a subpoena, or similar legal process; +* when they believe in good faith that disclosure is necessary to protect their rights, protect your safety or the safety of others, investigate fraud, or respond to a government request; +* with their trusted services providers who work on their behalf, do not have an independent use of the information we disclose to them, and have agreed to adhere to the rules set forth in this privacy statement. + +**Opt-Out Rights** + +You can stop all collection of information by the Application easily by uninstalling it. You may use the standard uninstall processes as may be available as part of your mobile device or via the mobile application marketplace or network. + +**Data Retention Policy** + +The Service Provider will retain User Provided data for as long as you use the Application and for a reasonable time thereafter. If you'd like them to delete User Provided Data that you have provided via the Application, please contact them at chochanaresh0@gmail.com and they will respond in a reasonable time. + +**Children** + +The Service Provider does not use the Application to knowingly solicit data from or market to children under the age of 13. + +The Service Provider does not knowingly collect personally identifiable information from children. The Service Provider encourages all children to never submit any personally identifiable information through the Application and/or Services. The Service Provider encourage parents and legal guardians to monitor their children's Internet usage and to help enforce this Policy by instructing their children never to provide personally identifiable information through the Application and/or Services without their permission. If you have reason to believe that a child has provided personally identifiable information to the Service Provider through the Application and/or Services, please contact the Service Provider (chochanaresh0@gmail.com) so that they will be able to take the necessary actions. You must also be at least 16 years of age to consent to the processing of your personally identifiable information in your country (in some countries we may allow your parent or guardian to do so on your behalf). + +**Security** + +The Service Provider is concerned about safeguarding the confidentiality of your information. The Service Provider provides physical, electronic, and procedural safeguards to protect information the Service Provider processes and maintains. + +**Changes** + +This Privacy Policy may be updated from time to time for any reason. The Service Provider will notify you of any changes to the Privacy Policy by updating this page with the new Privacy Policy. You are advised to consult this Privacy Policy regularly for any changes, as continued use is deemed approval of all changes. + +This privacy policy is effective as of 2025-06-24 + +**Your Consent** + +By using the Application, you are consenting to the processing of your information as set forth in this Privacy Policy now and as amended by us. + +**Contact Us** + +If you have any questions regarding privacy while using the Application, or have questions about the practices, please contact the Service Provider via email at chochanaresh0@gmail.com. + +* * * \ No newline at end of file From 064083785c49cc474851f47738ba9c15d132a806 Mon Sep 17 00:00:00 2001 From: NareshChocha Date: Tue, 24 Jun 2025 22:43:26 +0530 Subject: [PATCH 03/16] refactor: enhance file picker result handling and simplify intent creation --- .gitignore | 1 + .../FilePickerResultContracts.kt | 66 +++++-------------- .../activitys/DocumentFilePickerActivity.kt | 23 +++++-- .../ui/activitys/MediaFilePickerActivity.kt | 20 +++++- 4 files changed, 51 insertions(+), 59 deletions(-) diff --git a/.gitignore b/.gitignore index f068e07..40bda1d 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ .externalNativeBuild .cxx local.properties +/release-keystore.properties diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/FilePickerResultContracts.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/FilePickerResultContracts.kt index e847b08..5e41b93 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/FilePickerResultContracts.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/FilePickerResultContracts.kt @@ -3,7 +3,6 @@ package com.nareshchocha.filepickerlibrary import android.app.Activity import android.content.Context import android.content.Intent -import android.os.Build import androidx.activity.result.contract.ActivityResultContract import com.nareshchocha.filepickerlibrary.models.BaseConfig import com.nareshchocha.filepickerlibrary.models.DocumentFilePickerConfig @@ -17,12 +16,8 @@ import com.nareshchocha.filepickerlibrary.ui.activitys.ImageCaptureActivity import com.nareshchocha.filepickerlibrary.ui.activitys.MediaFilePickerActivity import com.nareshchocha.filepickerlibrary.ui.activitys.PopUpActivity import com.nareshchocha.filepickerlibrary.ui.activitys.VideoCaptureActivity -import com.nareshchocha.filepickerlibrary.utilities.FileUtils import com.nareshchocha.filepickerlibrary.utilities.appConst.Const import com.nareshchocha.filepickerlibrary.utilities.getClipDataUris -import com.nareshchocha.filepickerlibrary.utilities.getDocumentFilePick -import com.nareshchocha.filepickerlibrary.utilities.getFilePathList -import com.nareshchocha.filepickerlibrary.utilities.getMediaIntent /** * A collection of ActivityResultContracts for different file picking operations. @@ -90,21 +85,10 @@ class FilePickerResultContracts private constructor() { * Returns a FilePickerResult that contains the URIs and file paths of the selected media. */ class PickMedia : ActivityResultContract() { - private var context: Context? = null - override fun createIntent( context: Context, input: PickMediaConfig? - ): Intent { - this.context = context - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - context.getMediaIntent( - input ?: PickMediaConfig() - ) - } else { - MediaFilePickerActivity.getInstance(context, input ?: PickMediaConfig()) - } - } + ): Intent = MediaFilePickerActivity.getInstance(context, input ?: PickMediaConfig()) override fun parseResult( resultCode: Int, @@ -114,23 +98,17 @@ class FilePickerResultContracts private constructor() { FilePickerResult(errorMessage = "Media selection failed or cancelled") } else { if (intent.clipData != null) { - val uris = intent.getClipDataUris() - val filePaths = uris.getFilePathList(context!!) - if (uris.isEmpty()) { - FilePickerResult(errorMessage = "No media selected") - } else { - FilePickerResult( - selectedFileUris = uris, - selectedFilePaths = filePaths - ) - } + FilePickerResult( + selectedFileUris = intent.getClipDataUris(), + selectedFilePaths = intent.getStringArrayListExtra(Const.BundleExtras.FILE_PATH_LIST) + ) } else if (intent.data != null) { FilePickerResult( selectedFileUri = intent.data, - selectedFilePath = intent.data?.let { FileUtils.getRealPath(context!!, it) } + selectedFilePath = intent.getStringExtra(Const.BundleExtras.FILE_PATH) ) } else { - FilePickerResult(errorMessage = "No media selected") + FilePickerResult(errorMessage = "No file selected") } } } @@ -140,19 +118,10 @@ class FilePickerResultContracts private constructor() { * Returns a FilePickerResult that contains the URIs and file paths of the selected documents. */ class PickDocumentFile : ActivityResultContract() { - private var context: Context? = null - override fun createIntent( context: Context, input: DocumentFilePickerConfig? - ): Intent { - this.context = context - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - getDocumentFilePick(input ?: DocumentFilePickerConfig()) - } else { - DocumentFilePickerActivity.getInstance(context, input ?: DocumentFilePickerConfig()) - } - } + ): Intent = DocumentFilePickerActivity.getInstance(context, input ?: DocumentFilePickerConfig()) override fun parseResult( resultCode: Int, @@ -162,23 +131,17 @@ class FilePickerResultContracts private constructor() { FilePickerResult(errorMessage = "Document selection failed or cancelled") } else { if (intent.clipData != null) { - val uris = intent.getClipDataUris() - val filePaths = uris.getFilePathList(context!!) - if (uris.isEmpty()) { - FilePickerResult(errorMessage = "No document selected") - } else { - FilePickerResult( - selectedFileUris = uris, - selectedFilePaths = filePaths - ) - } + FilePickerResult( + selectedFileUris = intent.getClipDataUris(), + selectedFilePaths = intent.getStringArrayListExtra(Const.BundleExtras.FILE_PATH_LIST) + ) } else if (intent.data != null) { FilePickerResult( selectedFileUri = intent.data, - selectedFilePath = intent.data?.let { FileUtils.getRealPath(context!!, it) } + selectedFilePath = intent.getStringExtra(Const.BundleExtras.FILE_PATH) ) } else { - FilePickerResult(errorMessage = "No document selected") + FilePickerResult(errorMessage = "No file selected") } } } @@ -266,6 +229,7 @@ class FilePickerResultContracts private constructor() { resultCode, intent ) + else -> FilePickerResult(errorMessage = "Unknown file type") } } diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt index 5475d60..b185ed0 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt @@ -18,14 +18,17 @@ import com.nareshchocha.filepickerlibrary.R import com.nareshchocha.filepickerlibrary.models.DocumentFilePickerConfig import com.nareshchocha.filepickerlibrary.ui.components.dialogs.AppRationaleDialog import com.nareshchocha.filepickerlibrary.ui.components.dialogs.AppSettingDialog +import com.nareshchocha.filepickerlibrary.utilities.FileUtils import com.nareshchocha.filepickerlibrary.utilities.PermissionLists import com.nareshchocha.filepickerlibrary.utilities.SinglePermissionManager import com.nareshchocha.filepickerlibrary.utilities.appConst.Const import com.nareshchocha.filepickerlibrary.utilities.extensions.asString import com.nareshchocha.filepickerlibrary.utilities.extensions.getActivityOrNull +import com.nareshchocha.filepickerlibrary.utilities.getClipDataUris import com.nareshchocha.filepickerlibrary.utilities.getDocumentFilePick -import com.nareshchocha.filepickerlibrary.utilities.setActivityResult +import com.nareshchocha.filepickerlibrary.utilities.getFilePathList import com.nareshchocha.filepickerlibrary.utilities.setCanceledResult +import com.nareshchocha.filepickerlibrary.utilities.setSuccessResult internal class DocumentFilePickerActivity : ComponentActivity() { private val mDocumentFilePickerConfig: DocumentFilePickerConfig? by lazy { @@ -44,11 +47,19 @@ internal class DocumentFilePickerActivity : ComponentActivity() { registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> - setActivityResult( - resultCode = result.resultCode, - resultIntent = result.data, - false - ) + if (result.resultCode == RESULT_OK && result.data != null) { + if (mDocumentFilePickerConfig?.allowMultiple == true && result.data?.clipData != null) { + val uris = result.data?.getClipDataUris() + val filePaths = uris?.getFilePathList(this) + setSuccessResult(uris, filePath = filePaths) + } else if (result.data?.data != null) { + val data = result.data?.data + val filePath = data?.let { FileUtils.getRealPath(this, it) } + setSuccessResult(data, filePath) + } + } else { + setCanceledResult("File Picker Result Error: ${result.resultCode}") + } } override fun onCreate(savedInstanceState: Bundle?) { diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt index 75aefda..cfa5363 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt @@ -1,5 +1,6 @@ package com.nareshchocha.filepickerlibrary.ui.activitys +import android.app.Activity import android.content.Context import android.content.Intent import android.os.Build @@ -18,14 +19,17 @@ import com.nareshchocha.filepickerlibrary.R import com.nareshchocha.filepickerlibrary.models.PickMediaConfig import com.nareshchocha.filepickerlibrary.ui.components.dialogs.AppRationaleDialog import com.nareshchocha.filepickerlibrary.ui.components.dialogs.AppSettingDialog +import com.nareshchocha.filepickerlibrary.utilities.FileUtils import com.nareshchocha.filepickerlibrary.utilities.MediaMultiplePermissionManager import com.nareshchocha.filepickerlibrary.utilities.PermissionLists import com.nareshchocha.filepickerlibrary.utilities.appConst.Const import com.nareshchocha.filepickerlibrary.utilities.extensions.asString import com.nareshchocha.filepickerlibrary.utilities.extensions.getActivityOrNull +import com.nareshchocha.filepickerlibrary.utilities.getClipDataUris +import com.nareshchocha.filepickerlibrary.utilities.getFilePathList import com.nareshchocha.filepickerlibrary.utilities.getMediaIntent -import com.nareshchocha.filepickerlibrary.utilities.setActivityResult import com.nareshchocha.filepickerlibrary.utilities.setCanceledResult +import com.nareshchocha.filepickerlibrary.utilities.setSuccessResult internal class MediaFilePickerActivity : ComponentActivity() { private val mPickMediaConfig: PickMediaConfig? by lazy { @@ -44,7 +48,19 @@ internal class MediaFilePickerActivity : ComponentActivity() { registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> - setActivityResult(result.resultCode, result.data, false) + if (result.resultCode == Activity.RESULT_OK && result.data != null) { + if (mPickMediaConfig?.allowMultiple == true && result.data?.clipData != null) { + val uris = result.data?.getClipDataUris() + val filePaths = uris?.getFilePathList(this) + setSuccessResult(uris, filePath = filePaths) + } else if (result.data?.data != null) { + val data = result.data?.data + val filePath = data?.let { FileUtils.getRealPath(this, it) } + setSuccessResult(data, filePath) + } + } else { + setCanceledResult("File Picker Result Error: ${result.resultCode}") + } } override fun onCreate(savedInstanceState: Bundle?) { From 27e262014efb74f176c3b6d8c76bff9e38db8e60 Mon Sep 17 00:00:00 2001 From: NareshChocha Date: Tue, 24 Jun 2025 22:43:43 +0530 Subject: [PATCH 04/16] refactor: update app name format and enhance build configuration --- sample/.gitignore | 4 +++- sample/build.gradle.kts | 12 ++++++++---- sample/src/main/AndroidManifest.xml | 2 ++ sample/src/main/res/values/strings.xml | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/sample/.gitignore b/sample/.gitignore index 42afabf..0b0a500 100644 --- a/sample/.gitignore +++ b/sample/.gitignore @@ -1 +1,3 @@ -/build \ No newline at end of file +/build +/certificates +/release \ No newline at end of file diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index e420dab..9571b62 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -22,15 +22,18 @@ android { libs.versions.targetSdk .get() .toInt() - versionCode = 2 - versionName = "1.1" + versionCode = 1 + versionName = "0.4.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { - isMinifyEnabled = false + isMinifyEnabled = true + isShrinkResources = true + isJniDebuggable = false + isDebuggable = false proguardFiles( getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" @@ -66,7 +69,8 @@ dependencies { implementation(libs.androidx.material3) implementation(libs.androidx.material.icons.extended) - implementation("io.coil-kt.coil3:coil-compose:3.2.0") + // implementation("io.coil-kt.coil3:coil-compose:3.2.0") + // File Picker implementation(project(":filepickerlibrary")) implementation(libs.androidx.lifecycle.runtime.ktx) diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 4ee0712..4800817 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -17,6 +17,7 @@ android:fullBackupContent="@xml/backup_rules" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" + android:requestLegacyExternalStorage="true" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.FilePicker.Splash" @@ -30,6 +31,7 @@ + \ No newline at end of file diff --git a/sample/src/main/res/values/strings.xml b/sample/src/main/res/values/strings.xml index 5589fa3..9e01733 100644 --- a/sample/src/main/res/values/strings.xml +++ b/sample/src/main/res/values/strings.xml @@ -1,5 +1,5 @@ - FilePicker + File Picker Capture Image Capture Video Pick Images From 6c2ae729313ed2498004c9c7c3c35d6740e4290f Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Thu, 26 Jun 2025 18:33:09 +0530 Subject: [PATCH 05/16] feat: enhance sample app with file result display and update dependencies This commit introduces the following changes to the sample app: - Added a `FilePickerWithResultList` composable to display picked files. - Integrated the result list into the `AllFilePickers` composable to show selected images, videos, and other documents. - Refactored `AllFilePickers` to manage picked files in a list and update the UI accordingly. - Enabled Coil library for image loading. - Updated various dependencies including Android Gradle Plugin, Kotlin, Maven Publish, and Truth. --- gradle/libs.versions.toml | 8 +- sample/build.gradle.kts | 6 +- .../nareshchocha/filepicker/RootActivity.kt | 4 +- .../components/AppFilePickerResult.kt | 128 +++++++----------- .../components/FilePickerWithResultList.kt | 104 ++++++++++++++ 5 files changed, 162 insertions(+), 88 deletions(-) create mode 100644 sample/src/main/java/com/nareshchocha/filepicker/components/FilePickerWithResultList.kt diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f5a8e0c..686b8ad 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] -agp = "8.10.1" -mavenPublish = "0.25.3" -kotlin = "2.1.20" +agp = "8.11.0" +mavenPublish = "0.33.0" +kotlin = "2.2.0" detekt = "1.23.8" # build config @@ -24,7 +24,7 @@ espressoCore = "3.6.1" # testing -truth = "1.1.5" +truth = "1.4.4" lifecycleRuntimeKtx = "2.9.1" [libraries] diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 9571b62..564ea25 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -51,10 +51,6 @@ android { sourceCompatibility = JavaVersion.valueOf(libs.versions.jdkVersion.get()) targetCompatibility = JavaVersion.valueOf(libs.versions.jdkVersion.get()) } - - kotlinOptions { - jvmTarget = JavaVersion.valueOf(libs.versions.jdkVersion.get()).toString() - } } dependencies { @@ -69,7 +65,7 @@ dependencies { implementation(libs.androidx.material3) implementation(libs.androidx.material.icons.extended) - // implementation("io.coil-kt.coil3:coil-compose:3.2.0") + implementation("io.coil-kt.coil3:coil-compose:3.2.0") // File Picker implementation(project(":filepickerlibrary")) diff --git a/sample/src/main/java/com/nareshchocha/filepicker/RootActivity.kt b/sample/src/main/java/com/nareshchocha/filepicker/RootActivity.kt index 592d7ec..33ecbfd 100644 --- a/sample/src/main/java/com/nareshchocha/filepicker/RootActivity.kt +++ b/sample/src/main/java/com/nareshchocha/filepicker/RootActivity.kt @@ -19,7 +19,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen -import com.nareshchocha.filepicker.components.AllFilePicker +import com.nareshchocha.filepicker.components.AllFilePickers import com.nareshchocha.filepicker.ui.theme.FilePickerTheme import com.nareshchocha.filepickerlibrary.FilePickerResultContracts @@ -73,7 +73,7 @@ fun RootUI() { .fillMaxSize() .padding(innerPadding) ) { - AllFilePicker() + AllFilePickers() } } } diff --git a/sample/src/main/java/com/nareshchocha/filepicker/components/AppFilePickerResult.kt b/sample/src/main/java/com/nareshchocha/filepicker/components/AppFilePickerResult.kt index 321e9fd..d0d6c9b 100644 --- a/sample/src/main/java/com/nareshchocha/filepicker/components/AppFilePickerResult.kt +++ b/sample/src/main/java/com/nareshchocha/filepicker/components/AppFilePickerResult.kt @@ -1,113 +1,87 @@ package com.nareshchocha.filepicker.components -import android.util.Log +import android.net.Uri import androidx.activity.compose.rememberLauncherForActivityResult import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import com.nareshchocha.filepickerlibrary.FilePickerResultContracts import com.nareshchocha.filepickerlibrary.models.DocumentFilePickerConfig @Composable -fun FilePickerCaptureImage() { +fun AllFilePickers() { + var pickedFiles by remember { mutableStateOf(listOf()) } + val context = LocalContext.current + + // Helper to add a file to the list + fun addPickedFile( + uri: Uri?, + path: String? + ) { + uri ?: return + val mimeType = context.contentResolver.getType(uri) + val type = + when { + mimeType?.startsWith("image") == true -> "image" + mimeType?.startsWith("video") == true -> "video" + else -> "other" + } + pickedFiles = pickedFiles + PickedFile(uri, type, path) + } + val captureImageResultLauncher = rememberLauncherForActivityResult(FilePickerResultContracts.ImageCapture()) { result -> - Log.d("FilePicker", "Image Capture Result: $result") + addPickedFile(result?.selectedFileUri, result?.selectedFilePath) } - AppButton( - text = "Capture Image", - onClick = { - captureImageResultLauncher.launch(null) - } - ) -} - -@Composable -fun FilePickerCaptureVideo() { val captureVideoResultLauncher = rememberLauncherForActivityResult(FilePickerResultContracts.VideoCapture()) { result -> - Log.d("FilePicker", "Video Capture Result: $result") + addPickedFile(result?.selectedFileUri, result?.selectedFilePath) } - AppButton( - text = "Capture Video", - onClick = { - captureVideoResultLauncher.launch(null) - } - ) -} - -@Composable -fun FilePickerPickImage() { val pickImageResultLauncher = rememberLauncherForActivityResult(FilePickerResultContracts.PickMedia()) { result -> - Log.d("FilePicker", "Pick Image Result: $result") + addPickedFile(result?.selectedFileUri, result?.selectedFilePath) } - AppButton( - text = "Pick Image", - onClick = { - pickImageResultLauncher.launch(null) - } - ) -} - -@Composable -fun FilePickerPickDocument() { val pickDocumentResultLauncher = rememberLauncherForActivityResult(FilePickerResultContracts.PickDocumentFile()) { result -> - Log.d("FilePicker", "Pick Document Result: $result") + addPickedFile(result?.selectedFileUri, result?.selectedFilePath) } - AppButton( - text = "Pick Document", - onClick = { - pickDocumentResultLauncher.launch(null) - } - ) -} - -@Composable -fun FilePickerAllFilePicker() { - val pickDocumentResultLauncher = + val pickAllFilesResultLauncher = rememberLauncherForActivityResult(FilePickerResultContracts.AllFilePicker()) { result -> - Log.d("FilePicker", "Pick All Files Result: $result") - } - AppButton( - text = "Pick All Files", - onClick = { - pickDocumentResultLauncher.launch(null) + addPickedFile(result?.selectedFileUri, result?.selectedFilePath) } - ) -} - -@Composable -fun FilePickerAnyFilePicker() { - val pickDocumentResultLauncher = + val pickAnyFileResultLauncher = rememberLauncherForActivityResult(FilePickerResultContracts.AnyFilePicker()) { result -> - Log.d("FilePicker", "Pick Any File Result: $result") + addPickedFile(result?.selectedFileUri, result?.selectedFilePath) } - AppButton( - text = "Pick Any File", - onClick = { - pickDocumentResultLauncher.launch(DocumentFilePickerConfig()) - } - ) -} -@Preview -@Composable -fun AllFilePicker() { Column( modifier = Modifier.padding(horizontal = 24.dp, vertical = 16.dp), verticalArrangement = Arrangement.spacedBy(16.dp) ) { - FilePickerCaptureImage() - FilePickerCaptureVideo() - FilePickerPickImage() - FilePickerPickDocument() - FilePickerAllFilePicker() - FilePickerAnyFilePicker() + AppButton("Capture Image") { captureImageResultLauncher.launch(null) } + AppButton("Capture Video") { captureVideoResultLauncher.launch(null) } + AppButton("Pick Image") { pickImageResultLauncher.launch(null) } + AppButton("Pick Document") { pickDocumentResultLauncher.launch(null) } + AppButton("Pick All Files") { pickAllFilesResultLauncher.launch(null) } + AppButton("Pick Any File") { pickAnyFileResultLauncher.launch(DocumentFilePickerConfig()) } + + if (pickedFiles.isNotEmpty()) { + Spacer(Modifier.height(8.dp)) + Text("Selected Files:", style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold)) + FilePickerWithResultList(pickedFiles) + } } } diff --git a/sample/src/main/java/com/nareshchocha/filepicker/components/FilePickerWithResultList.kt b/sample/src/main/java/com/nareshchocha/filepicker/components/FilePickerWithResultList.kt new file mode 100644 index 0000000..462727a --- /dev/null +++ b/sample/src/main/java/com/nareshchocha/filepicker/components/FilePickerWithResultList.kt @@ -0,0 +1,104 @@ +package com.nareshchocha.filepicker.components + +import android.net.Uri +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Videocam +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import coil3.compose.rememberAsyncImagePainter + +@Composable +fun FilePickerWithResultList(pickedFiles: List) { + LazyColumn( + contentPadding = PaddingValues(top = 8.dp, bottom = 16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxHeight() + ) { + items(pickedFiles) { file -> + when (file.type) { + "image" -> + Image( + painter = rememberAsyncImagePainter(file.uri), + contentDescription = null, + modifier = Modifier.size(100.dp), + contentScale = ContentScale.Crop + ) + + "video" -> { + Row( + modifier = Modifier + .background( + color = MaterialTheme.colorScheme.secondaryContainer, + shape = RoundedCornerShape(8.dp) + ) + .padding(12.dp) + ) { + Icon( + imageVector = Icons.Default.Videocam, + contentDescription = "Video", + tint = Color(0xFF1976D2), + modifier = Modifier.padding(end = 8.dp) + ) + Text( + text = "Video: ${file.uri}", + style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Bold) + ) + } + } + + else -> { + Column( + modifier = + Modifier + .background( + color = MaterialTheme.colorScheme.outline, + shape = RoundedCornerShape(8.dp) + ) + .padding(12.dp) + ) { + Text( + text = "URI: ${file.uri}", + style = MaterialTheme.typography.bodyMedium.copy(fontWeight = FontWeight.Bold) + ) + Text( + text = "Path: ${file.filePath ?: "N/A"}", + style = MaterialTheme.typography.bodySmall + ) + } + } + } + } + } +} + +data class PickedFile( + val uri: Uri, + val type: String, // "image", "video", "other" + val filePath: String? = null +) + +@Preview +@Composable +fun PreviewFilePickerWithResultList() { + FilePickerWithResultList(emptyList()) +} From 351894307b4fc69fbadddb1aa5bb911e629be8f8 Mon Sep 17 00:00:00 2001 From: NareshChocha Date: Thu, 26 Jun 2025 21:18:35 +0530 Subject: [PATCH 06/16] refactor: update Proguard rules and enable edge-to-edge display This commit updates the Proguard rules for both the library and sample app to use a simpler configuration. It also enables edge-to-edge display in all Activities within the `filepickerlibrary` by calling `enableEdgeToEdge()` in their `onCreate` methods. Additionally, the sample app's version code and name have been incremented, and some minor UI adjustments have been made to the `FilePickerWithResultList` composable. Test dependencies and Kotlin JVM target settings have been removed from the library's build script. --- filepickerlibrary/build.gradle.kts | 22 ------------ filepickerlibrary/proguard-rules.pro | 35 +------------------ .../activitys/DocumentFilePickerActivity.kt | 2 ++ .../ui/activitys/ImageCaptureActivity.kt | 2 ++ .../ui/activitys/MediaFilePickerActivity.kt | 2 ++ .../ui/activitys/PopUpActivity.kt | 2 ++ .../ui/activitys/VideoCaptureActivity.kt | 2 ++ sample/build.gradle.kts | 4 +-- sample/proguard-rules.pro | 22 +----------- .../components/FilePickerWithResultList.kt | 18 +++++----- 10 files changed, 22 insertions(+), 89 deletions(-) diff --git a/filepickerlibrary/build.gradle.kts b/filepickerlibrary/build.gradle.kts index f3a7972..ff6ccc4 100644 --- a/filepickerlibrary/build.gradle.kts +++ b/filepickerlibrary/build.gradle.kts @@ -45,10 +45,6 @@ android { sourceCompatibility = JavaVersion.valueOf(libs.versions.jdkVersion.get()) targetCompatibility = JavaVersion.valueOf(libs.versions.jdkVersion.get()) } - - kotlinOptions { - jvmTarget = JavaVersion.valueOf(libs.versions.jdkVersion.get()).toString() - } } dependencies { @@ -73,24 +69,6 @@ dependencies { androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) - - // JUnit - testImplementation("junit:junit:4.13.2") - - // AndroidX Test - testImplementation("androidx.test:core:1.5.0") - testImplementation("androidx.test:runner:1.5.2") - testImplementation("androidx.test.ext:junit:1.1.5") - - // Robolectric for (Android framework simulation in unit tests) - testImplementation("org.robolectric:robolectric:4.10.3") - - // Mockito for mock(ing) - testImplementation("org.mockito:mockito-core:5.7.0") - testImplementation("org.mockito:mockito-inline:5.2.0") - // For mocking final classes) - // Optional: If you( need to mock Kotlin classes better) - testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1") } mavenPublishing { diff --git a/filepickerlibrary/proguard-rules.pro b/filepickerlibrary/proguard-rules.pro index a36673f..a8994a8 100644 --- a/filepickerlibrary/proguard-rules.pro +++ b/filepickerlibrary/proguard-rules.pro @@ -1,34 +1 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle.kts. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -# -renamesourcefileattribute SourceFile --keepclasseswithmembernames class com.nareshchocha.filepickerlibrary.models.**{ - *; -} -#-keepclassmembers class * extends com.nareshchocha.filepickerlibrary.models.BaseConfig { - # *; - #} - #-keepclassmembers class * extends com.nareshchocha.filepickerlibrary.models.PickMediaType { - # *; - #} - --keepclassmembers class * extends androidx.appcompat.app.AppCompatActivity { - *; -} +-keep,allowobfuscation class com.nareshchocha.filepickerlibrary.** {*;} \ No newline at end of file diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt index b185ed0..fd865f0 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt @@ -6,6 +6,7 @@ import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -64,6 +65,7 @@ internal class DocumentFilePickerActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + enableEdgeToEdge() if (mDocumentFilePickerConfig == null) { setCanceledResult(getString(R.string.document_file_picker_config_null_error)) return diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/ImageCaptureActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/ImageCaptureActivity.kt index e7fd261..1e6ee51 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/ImageCaptureActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/ImageCaptureActivity.kt @@ -7,6 +7,7 @@ import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -79,6 +80,7 @@ internal class ImageCaptureActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + enableEdgeToEdge() if (mImageCaptureConfig == null) { imageFile?.delete() setCanceledResult(getString(R.string.image_capture_config_null_error)) diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt index cfa5363..d17c04f 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt @@ -7,6 +7,7 @@ import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -65,6 +66,7 @@ internal class MediaFilePickerActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + enableEdgeToEdge() if (mPickMediaConfig == null) { setCanceledResult(getString(R.string.media_file_picker_config_null_error)) return diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/PopUpActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/PopUpActivity.kt index 493ae9e..f0857d7 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/PopUpActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/PopUpActivity.kt @@ -6,6 +6,7 @@ import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.compose.foundation.background import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.layout.Column @@ -68,6 +69,7 @@ internal class PopUpActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + enableEdgeToEdge() setContent { val pickerData = mPickerData if (pickerData == null) { diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/VideoCaptureActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/VideoCaptureActivity.kt index 62efeef..c0cc1e9 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/VideoCaptureActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/VideoCaptureActivity.kt @@ -7,6 +7,7 @@ import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -77,6 +78,7 @@ internal class VideoCaptureActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + enableEdgeToEdge() if (mVideoCaptureConfig == null) { videoFile?.delete() setCanceledResult(getString(R.string.image_capture_config_null_error)) diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index 564ea25..bdadb00 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -22,8 +22,8 @@ android { libs.versions.targetSdk .get() .toInt() - versionCode = 1 - versionName = "0.4.0" + versionCode = 2 + versionName = "0.5.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } diff --git a/sample/proguard-rules.pro b/sample/proguard-rules.pro index ff59496..1a4f7ea 100644 --- a/sample/proguard-rules.pro +++ b/sample/proguard-rules.pro @@ -1,21 +1 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle.kts. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file +-keep,allowobfuscation class com.nareshchocha.filepicker.** {*;} \ No newline at end of file diff --git a/sample/src/main/java/com/nareshchocha/filepicker/components/FilePickerWithResultList.kt b/sample/src/main/java/com/nareshchocha/filepicker/components/FilePickerWithResultList.kt index 462727a..9e1a365 100644 --- a/sample/src/main/java/com/nareshchocha/filepicker/components/FilePickerWithResultList.kt +++ b/sample/src/main/java/com/nareshchocha/filepicker/components/FilePickerWithResultList.kt @@ -20,7 +20,6 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview @@ -46,17 +45,17 @@ fun FilePickerWithResultList(pickedFiles: List) { "video" -> { Row( - modifier = Modifier - .background( - color = MaterialTheme.colorScheme.secondaryContainer, - shape = RoundedCornerShape(8.dp) - ) - .padding(12.dp) + modifier = + Modifier + .background( + color = MaterialTheme.colorScheme.secondaryContainer, + shape = RoundedCornerShape(8.dp) + ).padding(12.dp) ) { Icon( imageVector = Icons.Default.Videocam, contentDescription = "Video", - tint = Color(0xFF1976D2), + tint = MaterialTheme.colorScheme.outline, modifier = Modifier.padding(end = 8.dp) ) Text( @@ -73,8 +72,7 @@ fun FilePickerWithResultList(pickedFiles: List) { .background( color = MaterialTheme.colorScheme.outline, shape = RoundedCornerShape(8.dp) - ) - .padding(12.dp) + ).padding(12.dp) ) { Text( text = "URI: ${file.uri}", From 48da1132ec130b3ed2a92ef52966026b2e4d436d Mon Sep 17 00:00:00 2001 From: NareshChocha Date: Thu, 26 Jun 2025 23:37:11 +0530 Subject: [PATCH 07/16] feat: update Kotlin JVM target, dependencies, and remove annotations This commit updates the Kotlin JVM target to 21 for both the library and sample app. It also removes unused test dependencies and the `ExampleUnitTest.kt` file from the sample app. Additionally, the `@Keep` annotation has been removed from various classes and functions within the `filepickerlibrary` as it is no longer necessary with the updated Proguard rules. The GitHub Actions workflow has been updated to use JDK 21. The sample app's version code and name have been incremented, and packaging options have been added to exclude specific JNI libraries. --- .github/workflows/ci.yml | 12 +++---- filepickerlibrary/build.gradle.kts | 14 ++++----- .../filepickerlibrary/models/BaseConfig.kt | 2 -- .../filepickerlibrary/models/PickerData.kt | 12 +++---- .../filepickerlibrary/utilities/FileUtils.kt | 7 ----- .../utilities/PickerFileUtils.kt | 3 -- .../utilities/appConst/Const.kt | 3 -- .../extensions/FilePathExtensions.kt | 3 -- gradle/libs.versions.toml | 18 ----------- sample/build.gradle.kts | 31 ++++++++++++------- .../filepicker/ExampleUnitTest.kt | 16 ---------- 11 files changed, 35 insertions(+), 86 deletions(-) delete mode 100644 sample/src/test/java/com/nareshchocha/filepicker/ExampleUnitTest.kt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9ff1d74..2cc5f55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,11 +20,11 @@ jobs: - name: Checkout source uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Cache Gradle uses: actions/cache@v3 @@ -56,11 +56,11 @@ jobs: - name: Checkout source uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Cache Gradle uses: actions/cache@v3 @@ -105,11 +105,11 @@ jobs: - name: Checkout code uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: '17' + java-version: '21' - name: Make gradlew executable run: chmod +x ./gradlew diff --git a/filepickerlibrary/build.gradle.kts b/filepickerlibrary/build.gradle.kts index ff6ccc4..7b48290 100644 --- a/filepickerlibrary/build.gradle.kts +++ b/filepickerlibrary/build.gradle.kts @@ -1,4 +1,5 @@ import com.vanniktech.maven.publish.SonatypeHost +import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { alias(libs.plugins.android.library) @@ -45,6 +46,11 @@ android { sourceCompatibility = JavaVersion.valueOf(libs.versions.jdkVersion.get()) targetCompatibility = JavaVersion.valueOf(libs.versions.jdkVersion.get()) } + kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_21) + } + } } dependencies { @@ -57,16 +63,8 @@ dependencies { implementation(libs.androidx.material3) implementation(libs.androidx.material.icons.extended) - // testing - testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) - testImplementation(libs.truth) - androidTestImplementation(libs.truth) - // testing compose androidTestImplementation(platform(libs.androidx.compose.bom)) - androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) } diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/models/BaseConfig.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/models/BaseConfig.kt index 8112021..0552977 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/models/BaseConfig.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/models/BaseConfig.kt @@ -1,10 +1,8 @@ package com.nareshchocha.filepickerlibrary.models import android.os.Parcelable -import androidx.annotation.Keep import kotlinx.parcelize.Parcelize -@Keep() @Parcelize sealed class BaseConfig( open val popUpIcon: Int?, diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/models/PickerData.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/models/PickerData.kt index cdc0551..c5f5e16 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/models/PickerData.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/models/PickerData.kt @@ -4,21 +4,18 @@ import android.os.Build import android.os.Parcelable import android.provider.MediaStore import androidx.annotation.DrawableRes -import androidx.annotation.Keep import androidx.compose.runtime.Composable import com.nareshchocha.filepickerlibrary.R import com.nareshchocha.filepickerlibrary.utilities.appConst.Const import kotlinx.parcelize.Parcelize import java.io.File -@Keep @Parcelize data class PickerData( val mPopUpConfig: PopUpConfig? = null, val listIntents: List = emptyList() ) : Parcelable -@Keep @Parcelize data class PopUpConfig( val chooserTitle: String? = "Choose Option", @@ -40,7 +37,7 @@ enum class Orientation { @Parcelize data class ImageCaptureConfig( - @DrawableRes override val popUpIcon: Int? = R.drawable.ic_camera, + @param:DrawableRes override val popUpIcon: Int? = R.drawable.ic_camera, override val popUpText: String? = "Camera", val mFolder: File? = null, val fileName: String? = Const.DefaultPaths.defaultImageFile(), @@ -64,7 +61,7 @@ data class ImageCaptureConfig( @Parcelize data class VideoCaptureConfig( - @DrawableRes override val popUpIcon: Int? = R.drawable.ic_video, + @param:DrawableRes override val popUpIcon: Int? = R.drawable.ic_video, override val popUpText: String? = "Video", val mFolder: File? = null, val fileName: String? = Const.DefaultPaths.defaultVideoFile(), @@ -87,7 +84,7 @@ data class VideoCaptureConfig( @Parcelize data class PickMediaConfig( - @DrawableRes override val popUpIcon: Int? = R.drawable.ic_media, + @param:DrawableRes override val popUpIcon: Int? = R.drawable.ic_media, override val popUpText: String? = "Pick Media", val allowMultiple: Boolean? = false, /** @@ -124,7 +121,7 @@ data class PickMediaConfig( @Parcelize data class DocumentFilePickerConfig( - @DrawableRes override val popUpIcon: Int? = R.drawable.ic_file, + @param:DrawableRes override val popUpIcon: Int? = R.drawable.ic_file, override val popUpText: String? = "File Media", val allowMultiple: Boolean? = false, /** @@ -151,7 +148,6 @@ data class DocumentFilePickerConfig( ), Parcelable -@Keep @Parcelize enum class PopUpType : Parcelable { BOTTOM_SHEET, diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/FileUtils.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/FileUtils.kt index 529deac..5e5a3ea 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/FileUtils.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/FileUtils.kt @@ -7,7 +7,6 @@ import android.net.Uri import android.os.Environment import android.provider.DocumentsContract import android.provider.MediaStore -import androidx.annotation.Keep import androidx.core.net.toUri import com.nareshchocha.filepickerlibrary.utilities.appConst.Const import com.nareshchocha.filepickerlibrary.utilities.extensions.copyFileToInternalStorage @@ -21,9 +20,7 @@ import com.nareshchocha.filepickerlibrary.utilities.extensions.isGooglePhotosUri import com.nareshchocha.filepickerlibrary.utilities.extensions.isMediaDocument import java.io.File -@Keep internal object FileUtils { - @Keep fun getRealPath( context: Context, fileUri: Uri @@ -48,7 +45,6 @@ internal object FileUtils { null } - @Keep private fun pathFromURI( context: Context, uri: Uri @@ -126,7 +122,6 @@ internal object FileUtils { } } - @Keep private fun Context.getDownloadsDocumentPath(uri: Uri): String? { /*val fileName = getFileName(this, uri) if (fileName != null) { @@ -164,7 +159,6 @@ internal object FileUtils { return filePath ?: uri.path?.replaceFirst("^/document/raw:", "")?.replaceFirst("^raw:", "") } - @Keep private fun getExternalDocumentPath(uri: Uri): String { val docId = DocumentsContract.getDocumentId(uri) val split = @@ -186,7 +180,6 @@ internal object FileUtils { } } - @Keep private fun getFileName( context: Context, uri: Uri? diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/PickerFileUtils.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/PickerFileUtils.kt index fa0979e..69d72c9 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/PickerFileUtils.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/PickerFileUtils.kt @@ -2,7 +2,6 @@ package com.nareshchocha.filepickerlibrary.utilities import android.content.Context import android.net.Uri -import androidx.annotation.Keep import androidx.core.content.FileProvider import com.nareshchocha.filepickerlibrary.utilities.appConst.Const import java.io.File @@ -20,7 +19,6 @@ internal object PickerFileUtils { * @param fileName The name of the file to be created * @return A File object representing the newly created file path */ - @Keep fun createMediaFileFolder( folderFile: File, fileName: String @@ -64,7 +62,6 @@ internal object PickerFileUtils { * @param mFile The File object to create and get URI for * @return A content URI for the file or null if creation or URI generation fails */ - @Keep fun Context.createFileGetUri(mFile: File?): Uri? { if (mFile == null) { log("File is null, cannot create or get URI", priority = LogPriority.ERROR_LOG) diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/appConst/Const.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/appConst/Const.kt index e1e1586..3546f8f 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/appConst/Const.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/appConst/Const.kt @@ -2,14 +2,12 @@ package com.nareshchocha.filepickerlibrary.utilities.appConst import android.content.Context import android.os.Environment -import androidx.annotation.Keep import com.nareshchocha.filepickerlibrary.R import java.io.File /** * Contains constants and utility objects used throughout the File Picker library. */ -@Keep internal object Const { /** FileProvider authority suffix. */ const val AUTHORITY = ".library.fileprovider" @@ -85,7 +83,6 @@ internal object Const { /** * Keys for bundle extras exposed to external consumers. */ - @Keep object BundleExtras { /** Indicates if the file is from capture. */ const val FROM_CAPTURE = "isFromCapture" diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/extensions/FilePathExtensions.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/extensions/FilePathExtensions.kt index 7d100dc..053be38 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/extensions/FilePathExtensions.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/extensions/FilePathExtensions.kt @@ -6,7 +6,6 @@ import android.net.Uri import android.provider.DocumentsContract import android.provider.MediaStore import android.provider.OpenableColumns -import androidx.annotation.Keep import com.nareshchocha.filepickerlibrary.utilities.LogPriority import com.nareshchocha.filepickerlibrary.utilities.appConst.Const import com.nareshchocha.filepickerlibrary.utilities.log @@ -39,7 +38,6 @@ internal fun Uri.isGoogleDriveUri(): Boolean = "com.google.android.apps.docs.storage" == authority || "com.google.android.apps.docs.storage.legacy" == authority -@Keep internal fun getDataColumn( context: Context, uri: Uri?, @@ -71,7 +69,6 @@ internal fun getDataColumn( return null } -@Keep internal fun Context.getMediaDocumentPath(uri: Uri): String? { val docId = DocumentsContract.getDocumentId(uri) val split = diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 686b8ad..6c141f9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,16 +17,6 @@ core-splashscreen="1.0.1" activityCompose = "1.10.1" composeBom = "2025.06.01" -# Testing -junit = "4.13.2" -junitVersion = "1.2.1" -espressoCore = "3.6.1" - - -# testing -truth = "1.4.4" -lifecycleRuntimeKtx = "2.9.1" - [libraries] # core core-splashscreen = { group = "androidx.core", name = "core-splashscreen", version.ref = "core-splashscreen" } @@ -40,17 +30,9 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" } -# testing -junit = { group = "junit", name = "junit", version.ref = "junit" } -androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } -androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } # testing compose -androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } -# testing -truth = { module = "com.google.truth:truth", version.ref = "truth" } -androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" } diff --git a/sample/build.gradle.kts b/sample/build.gradle.kts index bdadb00..dbb4ba7 100644 --- a/sample/build.gradle.kts +++ b/sample/build.gradle.kts @@ -1,3 +1,5 @@ +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) @@ -22,12 +24,19 @@ android { libs.versions.targetSdk .get() .toInt() - versionCode = 2 - versionName = "0.5.0" + versionCode = 4 + versionName = "0.7.0" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } - + packaging { + jniLibs { + excludes.add("lib/arm64-v8a/libandroidx.graphics.path.so") + excludes.add("lib/armeabi-v7a/libandroidx.graphics.path.so") + excludes.add("lib/x86/libandroidx.graphics.path.so") + excludes.add("lib/x86_64/libandroidx.graphics.path.so") + } + } buildTypes { release { isMinifyEnabled = true @@ -40,6 +49,7 @@ android { ) } } + buildFeatures { compose = true aidl = false @@ -51,6 +61,11 @@ android { sourceCompatibility = JavaVersion.valueOf(libs.versions.jdkVersion.get()) targetCompatibility = JavaVersion.valueOf(libs.versions.jdkVersion.get()) } + kotlin { + compilerOptions { + jvmTarget.set(JvmTarget.JVM_21) + } + } } dependencies { @@ -69,18 +84,10 @@ dependencies { // File Picker implementation(project(":filepickerlibrary")) - implementation(libs.androidx.lifecycle.runtime.ktx) - - // testing - testImplementation(libs.junit) - androidTestImplementation(libs.androidx.junit) - androidTestImplementation(libs.androidx.espresso.core) - testImplementation(libs.truth) - androidTestImplementation(libs.truth) + // implementation(libs.androidx.lifecycle.runtime.ktx) // testing compose androidTestImplementation(platform(libs.androidx.compose.bom)) - androidTestImplementation(libs.androidx.ui.test.junit4) debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) } diff --git a/sample/src/test/java/com/nareshchocha/filepicker/ExampleUnitTest.kt b/sample/src/test/java/com/nareshchocha/filepicker/ExampleUnitTest.kt deleted file mode 100644 index a1e6f29..0000000 --- a/sample/src/test/java/com/nareshchocha/filepicker/ExampleUnitTest.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.nareshchocha.filepicker - -import org.junit.Assert.assertEquals -import org.junit.Test - -/** - * Example local unit test, which will execute on the development machine (host). - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -class ExampleUnitTest { - @Test - fun addition_isCorrect() { - assertEquals(4, 2 + 2) - } -} From cd173921ff0fcbb7b8a4b27fa049aa6f9996160f Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Fri, 27 Jun 2025 12:03:53 +0530 Subject: [PATCH 08/16] fix: simplify Maven Central publishing configuration --- filepickerlibrary/build.gradle.kts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/filepickerlibrary/build.gradle.kts b/filepickerlibrary/build.gradle.kts index 7b48290..5aa1c64 100644 --- a/filepickerlibrary/build.gradle.kts +++ b/filepickerlibrary/build.gradle.kts @@ -1,4 +1,3 @@ -import com.vanniktech.maven.publish.SonatypeHost import org.jetbrains.kotlin.gradle.dsl.JvmTarget plugins { @@ -70,7 +69,7 @@ dependencies { } mavenPublishing { - publishToMavenCentral(SonatypeHost.S01, true) + publishToMavenCentral( true) signAllPublications() coordinates("io.github.chochanaresh", "filepicker", versionName) From a4b5e444f7ec2f97aeaa32a851403ba142051ffd Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Fri, 27 Jun 2025 12:09:57 +0530 Subject: [PATCH 09/16] fix: update Maven Central publishing configuration to use automatic release flag --- filepickerlibrary/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/filepickerlibrary/build.gradle.kts b/filepickerlibrary/build.gradle.kts index 5aa1c64..a2564c5 100644 --- a/filepickerlibrary/build.gradle.kts +++ b/filepickerlibrary/build.gradle.kts @@ -67,9 +67,9 @@ dependencies { debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) } - +val automaticRelease: Boolean = true mavenPublishing { - publishToMavenCentral( true) + publishToMavenCentral(automaticRelease) signAllPublications() coordinates("io.github.chochanaresh", "filepicker", versionName) From f876430b16e493ca3f538c6ce296bd14938edac1 Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Fri, 27 Jun 2025 12:46:41 +0530 Subject: [PATCH 10/16] fix: update Maven Central publishing configuration to use automatic release flag --- .github/workflows/ci.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2cc5f55..8f3c4ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,13 @@ jobs: - name: Make gradlew executable run: chmod +x ./gradlew + - name: Restore gradle.properties + env: + GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }} + shell: bash + run: | + # Print the entire GRADLE_PROPERTIES value in logs + echo "GRADLE_PROPERTIES: $GRADLE_PROPERTIES" - name: Run checks run: ./gradlew filepickerlibrary:spotlessCheck detekt From 174b9c262038cf6a6df5cb1daf64ea6559c689bb Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Fri, 27 Jun 2025 12:50:12 +0530 Subject: [PATCH 11/16] feat: enhance CI configuration to manage Gradle properties and upload report --- .github/workflows/ci.yml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f3c4ac..dee39e8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -42,9 +42,16 @@ jobs: GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }} shell: bash run: | - # Print the entire GRADLE_PROPERTIES value in logs - echo "GRADLE_PROPERTIES: $GRADLE_PROPERTIES" + mkdir -p ~/.gradle/ + echo "GRADLE_USER_HOME=${HOME}/.gradle" >> $GITHUB_ENV + echo "${GRADLE_PROPERTIES}" > ~/.gradle/gradle.properties + - name: Upload gradle properties report + uses: actions/upload-artifact@v4 + with: + name: gradle-properties + path: | + **/.gradle/gradle.properties - name: Run checks run: ./gradlew filepickerlibrary:spotlessCheck detekt From bf2381fb224b125e2f251fdbf85aaf3bc8df4b6e Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Fri, 27 Jun 2025 12:51:49 +0530 Subject: [PATCH 12/16] fix: update CI configuration to correctly reference gradle.properties path --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dee39e8..1433092 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,7 +51,7 @@ jobs: with: name: gradle-properties path: | - **/.gradle/gradle.properties + **/gradle.properties - name: Run checks run: ./gradlew filepickerlibrary:spotlessCheck detekt From 2123815ac2a15baded52e864a51f1f32e5fcea4e Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Fri, 27 Jun 2025 14:05:20 +0530 Subject: [PATCH 13/16] fix: remove unnecessary steps for restoring and uploading gradle.properties in CI --- .github/workflows/ci.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1433092..2cc5f55 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,21 +37,7 @@ jobs: - name: Make gradlew executable run: chmod +x ./gradlew - - name: Restore gradle.properties - env: - GRADLE_PROPERTIES: ${{ secrets.GRADLE_PROPERTIES }} - shell: bash - run: | - mkdir -p ~/.gradle/ - echo "GRADLE_USER_HOME=${HOME}/.gradle" >> $GITHUB_ENV - echo "${GRADLE_PROPERTIES}" > ~/.gradle/gradle.properties - - name: Upload gradle properties report - uses: actions/upload-artifact@v4 - with: - name: gradle-properties - path: | - **/gradle.properties - name: Run checks run: ./gradlew filepickerlibrary:spotlessCheck detekt From e85edd368dbcd7c465fe38289c94fcb6693d2b96 Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Wed, 2 Jul 2025 10:58:45 +0530 Subject: [PATCH 14/16] feat: handle empty selection in file pickers Adds error messages and logic to file pickers to handle cases where no file is selected. - Includes new string resources for "no data" errors in document and media pickers. - Modifies `DocumentFilePickerActivity` and `MediaFilePickerActivity` to check for `clipData` when `data` is null and set a canceled result with the appropriate error message if no file is found. --- .../ui/activitys/DocumentFilePickerActivity.kt | 6 ++++++ .../ui/activitys/MediaFilePickerActivity.kt | 6 ++++++ filepickerlibrary/src/main/res/values/strings.xml | 2 ++ 3 files changed, 14 insertions(+) diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt index fd865f0..74de486 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt @@ -57,6 +57,12 @@ internal class DocumentFilePickerActivity : ComponentActivity() { val data = result.data?.data val filePath = data?.let { FileUtils.getRealPath(this, it) } setSuccessResult(data, filePath) + } else if (result.data?.clipData != null) { + val uri = result.data?.getClipDataUris()?.firstOrNull() + val filePath = uri?.let { FileUtils.getRealPath(this, it) } + setSuccessResult(uri, filePath) + } else { + setCanceledResult(getString(R.string.document_file_picker_no_data_error)) } } else { setCanceledResult("File Picker Result Error: ${result.resultCode}") diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt index d17c04f..acecb81 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt @@ -58,6 +58,12 @@ internal class MediaFilePickerActivity : ComponentActivity() { val data = result.data?.data val filePath = data?.let { FileUtils.getRealPath(this, it) } setSuccessResult(data, filePath) + } else if (result.data?.clipData != null) { + val uri = result.data?.getClipDataUris()?.firstOrNull() + val filePath = uri?.let { FileUtils.getRealPath(this, it) } + setSuccessResult(uri, filePath) + } else { + setCanceledResult(getString(R.string.media_file_picker_no_data_error)) } } else { setCanceledResult("File Picker Result Error: ${result.resultCode}") diff --git a/filepickerlibrary/src/main/res/values/strings.xml b/filepickerlibrary/src/main/res/values/strings.xml index d41f29f..2eead2e 100644 --- a/filepickerlibrary/src/main/res/values/strings.xml +++ b/filepickerlibrary/src/main/res/values/strings.xml @@ -48,9 +48,11 @@ The document picker configuration is missing. This app requires access to your storage in order to pick and manage documents. Please grant the storage permission to proceed. + No Document files found in the selected directory. The media file picker configuration is missing. This app requires access to your storage to pick media files such as images and videos. Please grant the storage permission to continue. + No media files found in the selected directory. \ No newline at end of file From 8526eaffb316b7901db738bf548ba3c8f5ca34b3 Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Tue, 5 Aug 2025 11:56:04 +0530 Subject: [PATCH 15/16] build: update Gradle and dependencies This commit updates the following: - Android Gradle Plugin to version 8.12.0 - Maven Publish plugin to version 0.34.0 - Compose BOM to version 2025.07.00 - Spotless plugin to version 7.2.1 - Gradle wrapper to version 8.14.3 --- gradle/libs.versions.toml | 8 ++++---- gradle/wrapper/gradle-wrapper.properties | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6c141f9..d11a880 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] -agp = "8.11.0" -mavenPublish = "0.33.0" +agp = "8.12.0" +mavenPublish = "0.34.0" kotlin = "2.2.0" detekt = "1.23.8" @@ -15,7 +15,7 @@ core-splashscreen="1.0.1" # compose activityCompose = "1.10.1" -composeBom = "2025.06.01" +composeBom = "2025.07.00" [libraries] # core @@ -44,7 +44,7 @@ kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "ko kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" } # code style review arturbosch-detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } -spotless = { id = "com.diffplug.spotless", version = "7.0.4" } +spotless = { id = "com.diffplug.spotless", version = "7.2.1" } # maven publish maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" } # signing diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b6571f8..51dfbfe 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Fri Nov 22 15:17:24 IST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From 607735f51fcc0a68cb1cbc5cb4ccf9a71ee0fc22 Mon Sep 17 00:00:00 2001 From: naresh chocha Date: Tue, 5 Aug 2025 11:56:50 +0530 Subject: [PATCH 16/16] refactor: use BundleCompat.getParcelable for safer Parcelable retrieval Replaces manual Android version checks and deprecated `getParcelableExtra` calls with `BundleCompat.getParcelable`. This simplifies the code and ensures type safety when retrieving Parcelable objects from Intent extras across different Android versions. - Updated in `PopUpActivity`, `MediaFilePickerActivity`, `DocumentFilePickerActivity`, `ImageCaptureActivity`, and `VideoCaptureActivity`. - Removed an unused `setActivityResult` function from `FilePickerResultHandler.kt`. --- .../activitys/DocumentFilePickerActivity.kt | 16 ++++++-------- .../ui/activitys/ImageCaptureActivity.kt | 16 ++++++-------- .../ui/activitys/MediaFilePickerActivity.kt | 16 ++++++-------- .../ui/activitys/PopUpActivity.kt | 16 ++++++-------- .../ui/activitys/VideoCaptureActivity.kt | 16 ++++++-------- .../utilities/FilePickerResultHandler.kt | 21 ------------------- 6 files changed, 30 insertions(+), 71 deletions(-) diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt index 74de486..91c0941 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/DocumentFilePickerActivity.kt @@ -2,7 +2,6 @@ package com.nareshchocha.filepickerlibrary.ui.activitys import android.content.Context import android.content.Intent -import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -15,6 +14,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.core.os.BundleCompat import com.nareshchocha.filepickerlibrary.R import com.nareshchocha.filepickerlibrary.models.DocumentFilePickerConfig import com.nareshchocha.filepickerlibrary.ui.components.dialogs.AppRationaleDialog @@ -33,15 +33,11 @@ import com.nareshchocha.filepickerlibrary.utilities.setSuccessResult internal class DocumentFilePickerActivity : ComponentActivity() { private val mDocumentFilePickerConfig: DocumentFilePickerConfig? by lazy { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra( - Const.BundleInternalExtras.PICK_DOCUMENT, - DocumentFilePickerConfig::class.java - ) - } else { - @Suppress("DEPRECATION") - intent.getParcelableExtra(Const.BundleInternalExtras.PICK_DOCUMENT) as DocumentFilePickerConfig? - } + BundleCompat.getParcelable( + intent.extras ?: Bundle.EMPTY, + Const.BundleInternalExtras.PICK_DOCUMENT, + DocumentFilePickerConfig::class.java + ) } val documentFilePickerLauncher = diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/ImageCaptureActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/ImageCaptureActivity.kt index 1e6ee51..e5f30e9 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/ImageCaptureActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/ImageCaptureActivity.kt @@ -3,7 +3,6 @@ package com.nareshchocha.filepickerlibrary.ui.activitys import android.content.Context import android.content.Intent import android.net.Uri -import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -16,6 +15,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.core.os.BundleCompat import com.nareshchocha.filepickerlibrary.R import com.nareshchocha.filepickerlibrary.models.ImageCaptureConfig import com.nareshchocha.filepickerlibrary.ui.components.dialogs.AppRationaleDialog @@ -35,15 +35,11 @@ import java.io.File internal class ImageCaptureActivity : ComponentActivity() { private val mImageCaptureConfig: ImageCaptureConfig? by lazy { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra( - Const.BundleInternalExtras.IMAGE_CAPTURE, - ImageCaptureConfig::class.java - ) - } else { - @Suppress("DEPRECATION") - intent.getParcelableExtra(Const.BundleInternalExtras.IMAGE_CAPTURE) as ImageCaptureConfig? - } + BundleCompat.getParcelable( + intent.extras ?: Bundle.EMPTY, + Const.BundleInternalExtras.IMAGE_CAPTURE, + ImageCaptureConfig::class.java + ) } private var imageFile: File? = null private val imageFileUri: Uri? by lazy { diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt index acecb81..f33d4ac 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/MediaFilePickerActivity.kt @@ -3,7 +3,6 @@ package com.nareshchocha.filepickerlibrary.ui.activitys import android.app.Activity import android.content.Context import android.content.Intent -import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -16,6 +15,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.core.os.BundleCompat import com.nareshchocha.filepickerlibrary.R import com.nareshchocha.filepickerlibrary.models.PickMediaConfig import com.nareshchocha.filepickerlibrary.ui.components.dialogs.AppRationaleDialog @@ -34,15 +34,11 @@ import com.nareshchocha.filepickerlibrary.utilities.setSuccessResult internal class MediaFilePickerActivity : ComponentActivity() { private val mPickMediaConfig: PickMediaConfig? by lazy { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra( - Const.BundleInternalExtras.PICK_MEDIA, - PickMediaConfig::class.java - ) - } else { - @Suppress("DEPRECATION") - intent.getParcelableExtra(Const.BundleInternalExtras.PICK_MEDIA) as PickMediaConfig? - } + BundleCompat.getParcelable( + intent.extras ?: Bundle.EMPTY, + Const.BundleInternalExtras.PICK_MEDIA, + PickMediaConfig::class.java + ) } val mediaFilePickerLauncher = diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/PopUpActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/PopUpActivity.kt index f0857d7..4abafbe 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/PopUpActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/PopUpActivity.kt @@ -2,7 +2,6 @@ package com.nareshchocha.filepickerlibrary.ui.activitys import android.content.Context import android.content.Intent -import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -24,6 +23,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.core.os.BundleCompat import com.nareshchocha.filepickerlibrary.FilePickerResultContracts import com.nareshchocha.filepickerlibrary.R import com.nareshchocha.filepickerlibrary.models.BaseConfig @@ -39,15 +39,11 @@ import com.nareshchocha.filepickerlibrary.utilities.toArrayList internal class PopUpActivity : ComponentActivity() { private val mPickerData: PickerData? by lazy { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra( - Const.BundleInternalExtras.PICKER_DATA, - PickerData::class.java - ) - } else { - @Suppress("DEPRECATION") - intent.getParcelableExtra(Const.BundleInternalExtras.PICKER_DATA) as PickerData? - } + BundleCompat.getParcelable( + intent.extras ?: Bundle.EMPTY, + Const.BundleInternalExtras.PICKER_DATA, + PickerData::class.java + ) } private val intentResultLauncher = diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/VideoCaptureActivity.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/VideoCaptureActivity.kt index c0cc1e9..bae1240 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/VideoCaptureActivity.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/ui/activitys/VideoCaptureActivity.kt @@ -3,7 +3,6 @@ package com.nareshchocha.filepickerlibrary.ui.activitys import android.content.Context import android.content.Intent import android.net.Uri -import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -16,6 +15,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import androidx.core.os.BundleCompat import com.nareshchocha.filepickerlibrary.R import com.nareshchocha.filepickerlibrary.models.VideoCaptureConfig import com.nareshchocha.filepickerlibrary.ui.components.dialogs.AppRationaleDialog @@ -35,15 +35,11 @@ import java.io.File internal class VideoCaptureActivity : ComponentActivity() { private val mVideoCaptureConfig: VideoCaptureConfig? by lazy { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - intent.getParcelableExtra( - Const.BundleInternalExtras.VIDEO_CAPTURE, - VideoCaptureConfig::class.java - ) - } else { - @Suppress("DEPRECATION") - intent.getParcelableExtra(Const.BundleInternalExtras.VIDEO_CAPTURE) as VideoCaptureConfig? - } + BundleCompat.getParcelable( + intent.extras ?: Bundle.EMPTY, + Const.BundleInternalExtras.VIDEO_CAPTURE, + VideoCaptureConfig::class.java + ) } private var videoFile: File? = null private val videoFileUri: Uri? by lazy { diff --git a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/FilePickerResultHandler.kt b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/FilePickerResultHandler.kt index 52bc793..408a897 100644 --- a/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/FilePickerResultHandler.kt +++ b/filepickerlibrary/src/main/java/com/nareshchocha/filepickerlibrary/utilities/FilePickerResultHandler.kt @@ -82,27 +82,6 @@ internal fun Activity.setSuccessResult( finish() } -internal fun Activity.setActivityResult( - resultCode: Int? = null, - resultIntent: Intent? = null, - isFromCapture: Boolean = false -) { - log("ResultIntent : $resultIntent", LogPriority.INFO_LOG, Const.LogTag.FILE_PICKER_RESULT) - setResult( - if (resultIntent == null || resultCode == null) Activity.RESULT_CANCELED else resultCode, - resultIntent?.apply { - flags = - if (isFromCapture) { - Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION - } else { - Intent.FLAG_GRANT_READ_URI_PERMISSION - } - if (isFromCapture) putExtra(Const.BundleExtras.FROM_CAPTURE, true) - } - ) - finish() -} - internal fun Activity.setCanceledResult(error: String? = null) { log("Error: $error", LogPriority.ERROR_LOG, Const.LogTag.FILE_PICKER_RESULT) setResult(