├── app ├── .gitignore ├── src │ ├── main │ │ ├── ic_launcher-web.png │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── drawable-hdpi │ │ │ │ ├── ic_action_camera.png │ │ │ │ ├── ic_action_close.png │ │ │ │ ├── ic_action_file.png │ │ │ │ ├── ic_action_name.png │ │ │ │ ├── ic_action_gallery.png │ │ │ │ └── ic_action_photo_camera.png │ │ │ ├── drawable-mdpi │ │ │ │ ├── ic_action_camera.png │ │ │ │ ├── ic_action_close.png │ │ │ │ ├── ic_action_file.png │ │ │ │ ├── ic_action_name.png │ │ │ │ ├── ic_action_gallery.png │ │ │ │ └── ic_action_photo_camera.png │ │ │ ├── drawable-xhdpi │ │ │ │ ├── ic_action_close.png │ │ │ │ ├── ic_action_file.png │ │ │ │ ├── ic_action_name.png │ │ │ │ ├── ic_action_camera.png │ │ │ │ ├── ic_action_gallery.png │ │ │ │ └── ic_action_photo_camera.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── ic_action_file.png │ │ │ │ ├── ic_action_name.png │ │ │ │ ├── ic_action_camera.png │ │ │ │ ├── ic_action_close.png │ │ │ │ ├── ic_action_gallery.png │ │ │ │ └── ic_action_photo_camera.png │ │ │ ├── menu │ │ │ │ └── main_menu.xml │ │ │ ├── values │ │ │ │ ├── dimens.xml │ │ │ │ ├── styles.xml │ │ │ │ ├── colors.xml │ │ │ │ └── strings.xml │ │ │ ├── layout │ │ │ │ ├── item_row_gallery.xml │ │ │ │ ├── item_row_layout.xml │ │ │ │ ├── activity_main.xml │ │ │ │ ├── activity_gallery.xml │ │ │ │ └── dialog_details.xml │ │ │ └── drawable │ │ │ │ └── background_text_view.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── androidbuffer │ │ │ └── kotlinfilepickersample │ │ │ ├── ThumbnailAdapter.kt │ │ │ ├── PickerAdapter.kt │ │ │ ├── DetailsDialog.kt │ │ │ ├── GalleryActivity.kt │ │ │ └── MainActivity.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── androidbuffer │ │ │ └── kotlinfilepickersample │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── androidbuffer │ │ └── kotlinfilepickersample │ │ └── ExampleInstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── kotlinfilepicker ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── xml │ │ │ │ └── provider_paths.xml │ │ │ └── values │ │ │ │ └── strings.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── androidbuffer │ │ │ └── kotlinfilepicker │ │ │ ├── KotConstants.kt │ │ │ ├── KotResult.kt │ │ │ ├── KotlinFilePicker.kt │ │ │ ├── KotRequest.kt │ │ │ └── KotUtil.kt │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── androidbuffer │ │ │ └── kotlinfilepicker │ │ │ └── KotUtilTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── androidbuffer │ │ └── kotlinfilepicker │ │ └── InstrumentedTest.kt ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── .gitignore ├── LICENSE ├── README.md └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /kotlinfilepicker/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':kotlinfilepicker' 2 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-hdpi/ic_action_camera.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-hdpi/ic_action_close.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-hdpi/ic_action_file.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-hdpi/ic_action_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-mdpi/ic_action_camera.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-mdpi/ic_action_close.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-mdpi/ic_action_file.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-mdpi/ic_action_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xhdpi/ic_action_close.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xhdpi/ic_action_file.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xhdpi/ic_action_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_file.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_name.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_name.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-hdpi/ic_action_gallery.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-mdpi/ic_action_gallery.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xhdpi/ic_action_camera.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xhdpi/ic_action_gallery.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_camera.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_close.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_gallery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_gallery.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_action_photo_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-hdpi/ic_action_photo_camera.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_action_photo_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-mdpi/ic_action_photo_camera.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_action_photo_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xhdpi/ic_action_photo_camera.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_action_photo_camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/androidbuffer/Kotlinfilepicker/HEAD/app/src/main/res/drawable-xxhdpi/ic_action_photo_camera.png -------------------------------------------------------------------------------- /kotlinfilepicker/src/main/res/xml/provider_paths.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #admob setup 2 | admobAppId=ca-app-pub-3940256099942544~3347511713 3 | admobDashboardAdId=ca-app-pub-3940256099942544/6300978111 4 | android.enableJetifier=true 5 | android.useAndroidX=true 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Jan 24 00:33:50 IST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/menu/main_menu.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20sp 4 | 8dp 5 | 16dp 6 | 4dp 7 | 80dp 8 | 2dp 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFC107 4 | #FFA000 5 | #FF4081 6 | #fafafa 7 | #212121 8 | #bdbdbd 9 | #fff 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_row_gallery.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /app/src/test/java/com/androidbuffer/kotlinfilepickersample/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepickersample 2 | 3 | import org.junit.Assert.assertEquals 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | class ExampleUnitTest { 12 | @Test 13 | fun addition_isCorrect() { 14 | assertEquals(4, 2 + 2) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_text_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /kotlinfilepicker/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Kotlin File Picker 3 | Kotlin file picker is not initialized with proper arguments 4 | File selection not supported 5 | No Activity found to handle Intent 6 | not now 7 | SETTINGS 8 | Allow Application to access to your device\'s photos, media and files. Tap settings > Permissions, and turn storage on. 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .gradle 38 | /.idea/workspace.xml 39 | /.idea/libraries 40 | .DS_Store 41 | /build 42 | .externalNativeBuild 43 | .idea/ 44 | 45 | #release apk folders 46 | /app/release 47 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /kotlinfilepicker/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/androidbuffer/kotlinfilepickersample/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepickersample 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.runner.AndroidJUnit4 5 | import org.junit.Assert.assertEquals 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | /** 10 | * Instrumented test, which will execute on an Android device. 11 | * 12 | * See [testing documentation](http://d.android.com/tools/testing). 13 | */ 14 | @RunWith(AndroidJUnit4::class) 15 | class ExampleInstrumentedTest { 16 | @Test 17 | fun useAppContext() { 18 | // Context of the app under test. 19 | val appContext = InstrumentationRegistry.getInstrumentation().context 20 | assertEquals("com.androidbuffer.kotlinfilepickersample", appContext.packageName) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /kotlinfilepicker/src/test/java/com/androidbuffer/kotlinfilepicker/KotUtilTest.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepicker 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | /** 7 | * Example local unit test, which will execute on the development machine (host). 8 | * 9 | * See [testing documentation](http://d.android.com/tools/testing). 10 | */ 11 | 12 | class KotUtilTest { 13 | 14 | val extension = "pdf" 15 | val url = "/storage/emulated/0/Download/3c45fcac801adcd03c00391e9dbb119b.${extension}" 16 | 17 | @Test 18 | fun isExtensionCorrect() { 19 | val extension = "pdf" 20 | val url = "/storage/emulated/0/Download/3c45fcac801adcd03c00391e9dbb119b.${extension}" 21 | val extensionExpected = KotUtil.getFileExtensionFromUrl(url) 22 | Assert.assertEquals(extension, extensionExpected) 23 | } 24 | 25 | @Test 26 | fun isDateFormatCorrect(){ 27 | val currentDate = System.currentTimeMillis() 28 | val formattedDate = KotUtil.getDateModified(currentDate) 29 | Assert.assertNotNull(formattedDate) 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /kotlinfilepicker/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 16 | 17 | 22 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /kotlinfilepicker/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | 5 | android { 6 | compileSdkVersion 29 7 | defaultConfig { 8 | minSdkVersion 15 9 | targetSdkVersion 29 10 | versionCode 4 11 | versionName "0.0.4" 12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 13 | } 14 | 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | 22 | } 23 | 24 | dependencies { 25 | implementation fileTree(dir: 'libs', include: ['*.jar']) 26 | implementation 'androidx.appcompat:appcompat:1.1.0' 27 | testImplementation 'junit:junit:4.12' 28 | androidTestImplementation 'androidx.test:runner:1.2.0' 29 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 30 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 31 | } 32 | 33 | repositories { 34 | mavenCentral() 35 | } 36 | 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 androidbuffer.com 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Kotlin File Picker 3 | Pick any below option to choose file 4 | Camera 5 | File 6 | Gallery 7 | Video 8 | Gallery (Single) 9 | Modified Date : %1$s 10 | Type : %1$s 11 | Location : %1$s 12 | Size : %1$s 13 | File Details 14 | Close 15 | 16 | //recyclerview options 17 | 18 | Gallery(Single) 19 | Gallery (Multiple) 20 | File (Single) 21 | File (Multiple) 22 | Camera 23 | Video 24 | 25 | 26 | -------------------------------------------------------------------------------- /kotlinfilepicker/src/main/java/com/androidbuffer/kotlinfilepicker/KotConstants.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepicker 2 | 3 | /** 4 | * Created by AndroidBuffer on 28/12/17. 5 | */ 6 | 7 | 8 | class KotConstants { 9 | 10 | companion object { 11 | //extra intent constants 12 | public const val EXTRA_MULTIPLE_ENABLED = "extraMultipleEnabled" 13 | public const val EXTRA_FILE_MIME_TYPE = "extraFileMimeType" 14 | public const val EXTRA_FILE_SELECTION = "extraFileSelection" 15 | public const val EXTRA_FILE_RESULTS = "extraFileResults" 16 | 17 | //file selection type 18 | public const val SELECTION_TYPE_CAMERA = "Gallery" 19 | public const val SELECTION_TYPE_GALLERY = "Camera" 20 | public const val SELECTION_TYPE_FILE = "File" 21 | public const val SELECTION_TYPE_VIDEO = "Video" 22 | 23 | //files types supported 24 | val FILE_TYPE_IMAGE_ALL = "image/*" 25 | val FILE_TYPE_VIDEO_ALL = "video/*" 26 | val FILE_TYPE_FILE_ALL = "*/*" 27 | val FILE_TYPE_PDF = "application/pdf" 28 | val FILE_TYPE_DOCX = "application/vnd.openxmlformats-officedocument.wordprocessingml.document" 29 | val FILE_TYPE_EXCEL = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" 30 | } 31 | } -------------------------------------------------------------------------------- /kotlinfilepicker/src/main/java/com/androidbuffer/kotlinfilepicker/KotResult.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepicker 2 | 3 | import android.net.Uri 4 | import android.os.Parcel 5 | import android.os.Parcelable 6 | 7 | /** 8 | * Created by AndroidBuffer on 13/4/18. 9 | */ 10 | data class KotResult(val uri: Uri 11 | , val name: String? 12 | , val size: String? 13 | , val location: String? 14 | , val type: String? 15 | , val modified: String?) : Parcelable { 16 | constructor(parcel: Parcel) : this( 17 | parcel.readParcelable(Uri::class.java.classLoader), 18 | parcel.readString(), 19 | parcel.readString(), 20 | parcel.readString(), 21 | parcel.readString(), 22 | parcel.readString()) { 23 | } 24 | 25 | override fun writeToParcel(parcel: Parcel, flags: Int) { 26 | parcel.writeParcelable(uri, flags) 27 | parcel.writeString(name) 28 | parcel.writeString(size) 29 | parcel.writeString(location) 30 | parcel.writeString(type) 31 | parcel.writeString(modified) 32 | } 33 | 34 | override fun describeContents(): Int { 35 | return 0 36 | } 37 | 38 | companion object CREATOR : Parcelable.Creator { 39 | override fun createFromParcel(parcel: Parcel): KotResult { 40 | return KotResult(parcel) 41 | } 42 | 43 | override fun newArray(size: Int): Array { 44 | return arrayOfNulls(size) 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/com/androidbuffer/kotlinfilepickersample/ThumbnailAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepickersample 2 | 3 | import androidx.recyclerview.widget.RecyclerView 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.ImageView 8 | import com.androidbuffer.kotlinfilepicker.KotResult 9 | 10 | /** 11 | * Created by AndroidBuffer on 24/6/18. 12 | */ 13 | 14 | class ThumbnailAdapter(listOfImages: ArrayList,listener:OnThumbnailListener) : RecyclerView.Adapter() { 15 | 16 | val localList = listOfImages 17 | val clickListener = listener 18 | 19 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 20 | val view = LayoutInflater.from(parent.context).inflate(R.layout.item_row_gallery, parent, false) 21 | return ViewHolder(view) 22 | } 23 | 24 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 25 | holder.imageView.setImageURI(localList.get(position).uri) 26 | } 27 | 28 | override fun getItemCount(): Int { 29 | return localList.size 30 | } 31 | 32 | inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { 33 | val imageView: ImageView 34 | 35 | init { 36 | imageView = itemView.findViewById(R.id.ivThumbnail) 37 | imageView.setOnClickListener((View.OnClickListener { 38 | clickListener.onThumbnailClick(adapterPosition) 39 | })) 40 | } 41 | } 42 | 43 | /** 44 | * interface for onClick listener 45 | */ 46 | interface OnThumbnailListener { 47 | fun onThumbnailClick(position: Int) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_row_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 19 | 20 | 27 | 28 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 21 | 22 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 29 9 | defaultConfig { 10 | applicationId "com.androidbuffer.kotlinfilepickersample" 11 | minSdkVersion 15 12 | targetSdkVersion 29 13 | versionCode 4 14 | versionName "0.0.4" 15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 16 | } 17 | buildTypes { 18 | release { 19 | minifyEnabled true 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | buildConfigField("String", "AD_MOB_APP_ID", "\"${admobAppId}\"") 22 | resValue "string", "AD_MOB_DASHBOARD_AD_ID", "${admobDashboardAdId}" 23 | } 24 | debug { 25 | buildConfigField("String", "AD_MOB_APP_ID", "\"${admobAppId}\"") 26 | resValue "string", "AD_MOB_DASHBOARD_AD_ID", "${admobDashboardAdId}" 27 | applicationIdSuffix ".debug" 28 | } 29 | } 30 | } 31 | 32 | dependencies { 33 | implementation fileTree(include: ['*.jar'], dir: 'libs') 34 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 35 | implementation 'androidx.appcompat:appcompat:1.1.0' 36 | implementation 'androidx.recyclerview:recyclerview:1.0.0' 37 | implementation 'androidx.cardview:cardview:1.0.0' 38 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 39 | testImplementation 'junit:junit:4.12' 40 | androidTestImplementation 'androidx.test:runner:1.2.0' 41 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 42 | testImplementation 'org.mockito:mockito-core:3.0.0' 43 | androidTestImplementation 'org.mockito:mockito-android:3.0.0' 44 | testImplementation 'com.nhaarman:mockito-kotlin:1.6.0' 45 | implementation 'com.google.android.gms:play-services-ads:18.2.0' 46 | implementation project(':kotlinfilepicker') 47 | } 48 | -------------------------------------------------------------------------------- /kotlinfilepicker/src/androidTest/java/com/androidbuffer/kotlinfilepicker/InstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepicker 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import androidx.test.platform.app.InstrumentationRegistry 6 | import androidx.test.ext.junit.runners.AndroidJUnit4 7 | import org.junit.Assert.assertEquals 8 | import org.junit.Assert.assertNotNull 9 | import org.junit.Before 10 | import org.junit.Test 11 | import org.junit.runner.RunWith 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see [Testing documentation](http://d.android.com/tools/testing) 17 | */ 18 | @RunWith(AndroidJUnit4::class) 19 | class InstrumentedTest { 20 | 21 | val extension = "pdf" 22 | val url = "/storage/emulated/0/Download/3c45fcac801adcd03c00391e9dbb119b.${extension}" 23 | lateinit var appContext: Context 24 | 25 | @Before 26 | fun inItValues() { 27 | appContext = InstrumentationRegistry.getTargetContext() 28 | } 29 | 30 | @Test 31 | @Throws(Exception::class) 32 | fun useAppContext() { 33 | // Context of the app under test. 34 | assertEquals("com.androidbuffer.kotlinfilepicker.test", appContext.packageName) 35 | } 36 | 37 | @Test 38 | fun isMimeCorrect() { 39 | val correct = "application/pdf" 40 | val result = KotUtil.getMimeType(url) 41 | assertEquals(correct, result) 42 | } 43 | 44 | @Test 45 | fun fileIntentNotNull() { 46 | val mimeType = "image/*" 47 | val intent = KotUtil.getFileIntent(mimeType, true) 48 | assertNotNull(intent) 49 | } 50 | 51 | @Test 52 | fun galleryIntentNotNull() { 53 | val mimeType = "application/pdf" 54 | val intent = KotUtil.getGalleryIntent(mimeType, true) 55 | assertNotNull(intent) 56 | } 57 | 58 | @Test 59 | fun videoIntentNotNull(){ 60 | assertNotNull(KotUtil.getVideoIntent(appContext)) 61 | } 62 | 63 | @Test 64 | fun cameraIntentNotNull(){ 65 | assertNotNull(KotUtil.getCameraIntent(appContext)) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_gallery.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 22 | 23 | 31 | 32 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidbuffer/kotlinfilepickersample/PickerAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepickersample 2 | 3 | import android.view.LayoutInflater 4 | import android.view.View 5 | import android.view.ViewGroup 6 | import android.widget.ImageView 7 | import android.widget.TextView 8 | import androidx.recyclerview.widget.RecyclerView 9 | 10 | /** 11 | * Created by AndroidBuffer on 26/1/18. 12 | */ 13 | class PickerAdapter(titleArray: Array, 14 | drawableArray: Array, 15 | clickItemListener: OnClickItemListener) : RecyclerView.Adapter() { 16 | 17 | val arrayList: Array = titleArray 18 | val drawableList: Array = drawableArray 19 | val itemClickListener: OnClickItemListener = clickItemListener 20 | 21 | override fun onBindViewHolder(holder: ViewHolder, position: Int) { 22 | //map values to views here 23 | holder.tvTitle.text = arrayList[position] 24 | holder.ivTitleIcon.setImageResource(drawableList[position]) 25 | } 26 | 27 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { 28 | return ViewHolder(LayoutInflater.from(parent?.context).inflate(R.layout.item_row_layout, parent, false)) 29 | } 30 | 31 | override fun getItemCount(): Int { 32 | return arrayList.size; 33 | } 34 | 35 | inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), View.OnClickListener { 36 | //here goes view holder 37 | var tvTitle: TextView 38 | var ivTitleIcon: ImageView 39 | 40 | init { 41 | //set the item click listener 42 | tvTitle = itemView.findViewById(R.id.tvTitle) 43 | ivTitleIcon = itemView.findViewById(R.id.ivTitleIcon) 44 | itemView.setOnClickListener(this) 45 | } 46 | 47 | override fun onClick(p0: View) { 48 | //handle the click listener on items 49 | itemClickListener.onItemClick(adapterPosition) 50 | } 51 | } 52 | 53 | interface OnClickItemListener { 54 | fun onItemClick(position: Int) 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/dialog_details.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 21 | 22 | 29 | 30 | 37 | 38 | 45 | 46 | 53 | 54 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidbuffer/kotlinfilepickersample/DetailsDialog.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepickersample 2 | 3 | import android.app.DialogFragment 4 | import android.os.Bundle 5 | import androidx.appcompat.widget.AppCompatButton 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import android.view.WindowManager 10 | import android.widget.TextView 11 | import com.androidbuffer.kotlinfilepicker.KotResult 12 | 13 | /** 14 | * Created by AndroidBuffer on 13/4/18. 15 | */ 16 | class DetailsDialog : DialogFragment() { 17 | 18 | private lateinit var nameOfFile: TextView 19 | private lateinit var sizeOfFile: TextView 20 | private lateinit var locationOfFile: TextView 21 | private lateinit var typeOfFile: TextView 22 | private lateinit var modifiedDateOfFile: TextView 23 | 24 | companion object { 25 | 26 | val EXTRA_RESULT_OF_FILE = "EXTRA_FILE_RESULT" 27 | 28 | fun getInstance(kotResult: KotResult): DetailsDialog { 29 | val fragment = DetailsDialog() 30 | val bundle = Bundle() 31 | bundle.putParcelable(EXTRA_RESULT_OF_FILE, kotResult) 32 | fragment.arguments = bundle 33 | return fragment 34 | } 35 | } 36 | 37 | override fun onStart() { 38 | super.onStart() 39 | dialog.getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT) 40 | isCancelable = false 41 | } 42 | 43 | override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View { 44 | val view = inflater?.inflate(R.layout.dialog_details, container, false) 45 | return view!! 46 | } 47 | 48 | override fun onViewCreated(view: View?, savedInstanceState: Bundle?) { 49 | super.onViewCreated(view, savedInstanceState) 50 | assert(arguments != null) 51 | if (view == null) return 52 | //view init 53 | nameOfFile = view.findViewById(R.id.tvNameOfFile) 54 | sizeOfFile = view.findViewById(R.id.tvSizeOfFile) 55 | locationOfFile = view.findViewById(R.id.tvLocationOfFile) 56 | typeOfFile = view.findViewById(R.id.tvTypeOfFile) 57 | modifiedDateOfFile = view.findViewById(R.id.tvModiedDateOfFile) 58 | 59 | view.findViewById(R.id.btClose).setOnClickListener({ dismiss() }) 60 | 61 | val kotResult = arguments?.getParcelable(EXTRA_RESULT_OF_FILE) 62 | nameOfFile.text = kotResult?.name 63 | sizeOfFile.text = String.format(getString(R.string.size_of_file, kotResult?.size)) 64 | locationOfFile.text = String.format(getString(R.string.location_of_file, kotResult?.location)) 65 | typeOfFile.text = String.format(getString(R.string.type_of_file, kotResult?.type)) 66 | modifiedDateOfFile.text = String.format(getString(R.string.modified_date, kotResult?.modified)) 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidbuffer/kotlinfilepickersample/GalleryActivity.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepickersample 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.recyclerview.widget.LinearLayoutManager 6 | import androidx.recyclerview.widget.RecyclerView 7 | import android.util.Log 8 | import android.view.Menu 9 | import android.view.MenuItem 10 | import android.widget.ImageView 11 | import com.androidbuffer.kotlinfilepicker.KotResult 12 | import com.google.android.gms.ads.AdRequest 13 | import com.google.android.gms.ads.AdView 14 | 15 | 16 | /** 17 | * Created by AndroidBuffer on 26/1/18. 18 | */ 19 | class GalleryActivity : AppCompatActivity(), ThumbnailAdapter.OnThumbnailListener { 20 | 21 | private val EXTRA_IMAGE_RESULT = "EXTRA_IMAGE_RESULT" 22 | lateinit var rvThumbnailsImages: androidx.recyclerview.widget.RecyclerView 23 | lateinit var listOfImages: ArrayList 24 | lateinit var adapter: ThumbnailAdapter 25 | lateinit var imageViewFullScreen: ImageView 26 | lateinit var adViewBottom: AdView 27 | 28 | override fun onCreate(savedInstanceState: Bundle?) { 29 | super.onCreate(savedInstanceState) 30 | setContentView(R.layout.activity_gallery) 31 | listOfImages = intent.getParcelableArrayListExtra(EXTRA_IMAGE_RESULT) 32 | 33 | if (listOfImages.size > 1) { 34 | setupRecyclerView() 35 | } 36 | 37 | //image view 38 | imageViewFullScreen = findViewById(R.id.ivFullScreenImage) 39 | 40 | //by default image 41 | imageViewFullScreen.setImageURI(listOfImages.get(0).uri) 42 | 43 | setupAdView() 44 | } 45 | 46 | fun setupAdView() { 47 | //setup the adview 48 | adViewBottom = findViewById(R.id.adViewBottom) 49 | val adRequest = AdRequest.Builder().build() 50 | adViewBottom.loadAd(adRequest) 51 | } 52 | 53 | private fun setupRecyclerView() { 54 | //adapter for thumbnail 55 | adapter = ThumbnailAdapter(listOfImages, this) 56 | rvThumbnailsImages = findViewById(R.id.rvThumbnailsImages) 57 | rvThumbnailsImages.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this, 58 | androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL, false) 59 | rvThumbnailsImages.adapter = adapter 60 | } 61 | 62 | override fun onCreateOptionsMenu(menu: Menu?): Boolean { 63 | menuInflater.inflate(R.menu.main_menu, menu); 64 | return true 65 | } 66 | 67 | override fun onOptionsItemSelected(item: MenuItem?): Boolean { 68 | if (item?.itemId == R.id.menuClose) { 69 | finish() 70 | } 71 | return super.onOptionsItemSelected(item) 72 | } 73 | 74 | /** 75 | * on click for image thumbnail 76 | */ 77 | override fun onThumbnailClick(position: Int) { 78 | imageViewFullScreen.setImageURI(listOfImages.get(position).uri) 79 | Log.d("TAG", listOfImages.get(position).uri.toString()) 80 | } 81 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kotlinfilepicker [![](https://jitpack.io/v/androidbuffer/kotlinfilepicker.svg)](https://jitpack.io/#androidbuffer/kotlinfilepicker) 2 | Get it on Google Play 3 | 4 | An Android file picker written all in [Kotlin](https://kotlinlang.org/docs/reference/). KotlinFilePicker can be used to pick media files from Gallery and storage device. 5 | 6 | ## How to Download: 7 | Download the latest .aar and sample .apk from [release](https://github.com/androidbuffer/Kotlinfilepicker/releases). 8 | 9 | Gradle 10 | 11 | Add it in your root build.gradle at the end of repositories: 12 | ``` 13 | allprojects { 14 | repositories { 15 | ... 16 | maven { 17 | url 'https://jitpack.io' 18 | } 19 | } 20 | } 21 | ``` 22 | Add the dependency 23 | ``` 24 | dependencies { 25 | implementation 'com.github.androidbuffer:kotlinfilepicker:v0.0.4-alpha' 26 | } 27 | ``` 28 | Maven 29 | ``` 30 | 31 | 32 | jitpack.io 33 | https://jitpack.io 34 | 35 | 36 | ``` 37 | Add the dependency 38 | ``` 39 | 40 | com.github.androidbuffer 41 | kotlinfilepicker 42 | v0.0.1-alpha 43 | 44 | ``` 45 | ## Usage 46 | * make a camera request 47 | ``` 48 | KotRequest.Camera(this, REQUEST_CAMERA).pick() 49 | //or 50 | KotRequest.Camera(this).setRequestCode(REQUEST_CAMERA).pick() 51 | //or get a intent back 52 | var intent = KotRequest.Camera(this, REQUEST_CAMERA).getCameraIntent() 53 | ``` 54 | * make a video request 55 | ``` 56 | KotRequest.Video(this, REQUEST_VIDEO).pick() 57 | ``` 58 | * make a gallery request 59 | ``` 60 | KotRequest.Gallery(this, REQUEST_GALLERY).isMultiple(isMultiple).pick() 61 | ``` 62 | * make a file pick request 63 | ``` 64 | KotRequest.File(this, REQUEST_FILE) 65 | .isMultiple(isMultiple) 66 | .setMimeType(KotConstants.FILE_TYPE_FILE_ALL) 67 | .pick() 68 | ``` 69 | * Get back results like this. 70 | ``` 71 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 72 | super.onActivityResult(requestCode, resultCode, data) 73 | 74 | if (REQUEST_CAMERA == requestCode && resultCode == Activity.RESULT_OK) { 75 | 76 | val result = data?.getParcelableArrayListExtra(KotConstants.EXTRA_FILE_RESULTS) 77 | startGalleryView(result!!) 78 | 79 | } else if (REQUEST_FILE == requestCode && resultCode == Activity.RESULT_OK) { 80 | 81 | val result = data?.getParcelableArrayListExtra(KotConstants.EXTRA_FILE_RESULTS) 82 | createDetailsFromResult(result!!.get(0)) 83 | 84 | } else if (REQUEST_GALLERY == requestCode && resultCode == Activity.RESULT_OK) { 85 | 86 | val result = data?.getParcelableArrayListExtra(KotConstants.EXTRA_FILE_RESULTS) 87 | startGalleryView(result!!) 88 | 89 | } else if (REQUEST_VIDEO == requestCode && resultCode == Activity.RESULT_OK) { 90 | 91 | val result = data?.getParcelableArrayListExtra(KotConstants.EXTRA_FILE_RESULTS) 92 | createDetailsFromResult(result!!.get(0)) 93 | } 94 | } 95 | 96 | ``` 97 | 98 | ## Contributing / Issues 99 | we would be thankful for contributing to this project or if you find some bug or suggestions we welcome you also. 100 | 101 | ## Authors 102 | 103 | * **Vikas kumar** 104 | 105 | See also the list of [contributors](https://github.com/androidbuffer/Kotlinfilepicker/graphs/contributors) who participated in this project. 106 | 107 | ## License 108 | 109 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE) file for details 110 | -------------------------------------------------------------------------------- /app/src/main/java/com/androidbuffer/kotlinfilepickersample/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepickersample 2 | 3 | import android.app.Activity 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import androidx.appcompat.app.AppCompatActivity 7 | import androidx.recyclerview.widget.LinearLayoutManager 8 | import androidx.recyclerview.widget.RecyclerView 9 | import com.androidbuffer.kotlinfilepicker.KotConstants 10 | import com.androidbuffer.kotlinfilepicker.KotRequest 11 | import com.androidbuffer.kotlinfilepicker.KotResult 12 | import com.google.android.gms.ads.AdRequest 13 | import com.google.android.gms.ads.AdView 14 | import com.google.android.gms.ads.MobileAds 15 | 16 | 17 | class MainActivity : AppCompatActivity(), PickerAdapter.OnClickItemListener { 18 | 19 | private val EXTRA_IMAGE_RESULT = "EXTRA_IMAGE_RESULT" 20 | lateinit var rvFilePickerMain: androidx.recyclerview.widget.RecyclerView 21 | private val REQUEST_CAMERA = 101 22 | private val REQUEST_GALLERY = 102 23 | private val REQUEST_FILE = 103 24 | private val REQUEST_VIDEO = 104 25 | private var adapter: PickerAdapter? = null 26 | lateinit var titleArray: Array 27 | lateinit var adBottom: AdView 28 | 29 | private val drawableArray = arrayOf(R.drawable.ic_action_gallery, 30 | R.drawable.ic_action_gallery, 31 | R.drawable.ic_action_file, 32 | R.drawable.ic_action_file, 33 | R.drawable.ic_action_photo_camera, 34 | R.drawable.ic_action_camera) 35 | 36 | override fun onCreate(savedInstanceState: Bundle?) { 37 | super.onCreate(savedInstanceState) 38 | setContentView(R.layout.activity_main) 39 | //init the view elements 40 | rvFilePickerMain = findViewById(R.id.rvFilePickerMain) 41 | adBottom = findViewById(R.id.adViewBottom) 42 | titleArray = resources.getStringArray(R.array.arrayOptions) 43 | setRecyclerView() 44 | setupAdView() 45 | } 46 | 47 | private fun setupAdView() { 48 | //here setup the adview 49 | MobileAds.initialize(this, BuildConfig.AD_MOB_APP_ID) 50 | val adRequest = AdRequest.Builder().addTestDevice("D50CED5C7D63D1D9B4DE1A5251B16354").build() 51 | adBottom.loadAd(adRequest) 52 | } 53 | 54 | private fun setRecyclerView() { 55 | //here set the recycler view 56 | adapter = PickerAdapter(titleArray, drawableArray, this) 57 | rvFilePickerMain.layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this) 58 | rvFilePickerMain.adapter = adapter 59 | } 60 | 61 | private fun openCamera() { 62 | //opens camera from camera class 63 | KotRequest.Camera(this, REQUEST_CAMERA).pick() 64 | } 65 | 66 | private fun openVideo() { 67 | //opens a camera intent 68 | KotRequest.Video(this, REQUEST_VIDEO).pick() 69 | } 70 | 71 | private fun openGallery(isMultiple: Boolean) { 72 | //opens a gallery intent 73 | KotRequest.Gallery(this, REQUEST_GALLERY).isMultiple(isMultiple).pick() 74 | } 75 | 76 | private fun openFile(isMultiple: Boolean) { 77 | //opens a file intent 78 | KotRequest.File(this, REQUEST_FILE).isMultiple(isMultiple).setMimeType(KotConstants.FILE_TYPE_FILE_ALL).pick() 79 | } 80 | 81 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 82 | super.onActivityResult(requestCode, resultCode, data) 83 | 84 | if (REQUEST_CAMERA == requestCode && resultCode == Activity.RESULT_OK) { 85 | 86 | val result = data?.getParcelableArrayListExtra(KotConstants.EXTRA_FILE_RESULTS) 87 | startGalleryView(result!!) 88 | 89 | } else if (REQUEST_FILE == requestCode && resultCode == Activity.RESULT_OK) { 90 | 91 | val result = data?.getParcelableArrayListExtra(KotConstants.EXTRA_FILE_RESULTS) 92 | createDetailsFromResult(result!!.get(0)) 93 | 94 | } else if (REQUEST_GALLERY == requestCode && resultCode == Activity.RESULT_OK) { 95 | 96 | val result = data?.getParcelableArrayListExtra(KotConstants.EXTRA_FILE_RESULTS) 97 | startGalleryView(result!!) 98 | 99 | } else if (REQUEST_VIDEO == requestCode && resultCode == Activity.RESULT_OK) { 100 | 101 | val result = data?.getParcelableArrayListExtra(KotConstants.EXTRA_FILE_RESULTS) 102 | createDetailsFromResult(result!!.get(0)) 103 | } 104 | } 105 | 106 | fun startGalleryView(kotResultList: ArrayList) { 107 | val intent = Intent(this, GalleryActivity::class.java) 108 | intent.putExtra(EXTRA_IMAGE_RESULT, kotResultList) 109 | startActivity(intent) 110 | } 111 | 112 | fun createDetailsFromResult(kotResult: KotResult) { 113 | //this function creates the details dialog from the result 114 | val detailsDialog = DetailsDialog.getInstance(kotResult) 115 | detailsDialog.show(fragmentManager, "DetailsDialog") 116 | } 117 | 118 | override fun onItemClick(position: Int) { 119 | //listener for items in adapter 120 | when (position) { 121 | 0 -> openGallery(false) 122 | 1 -> openGallery(true) 123 | 2 -> openFile(false) 124 | 3 -> openFile(true) 125 | 4 -> openCamera() 126 | 5 -> openVideo() 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /kotlinfilepicker/src/main/java/com/androidbuffer/kotlinfilepicker/KotlinFilePicker.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepicker 2 | 3 | import android.Manifest 4 | import android.app.Activity 5 | import android.content.Context 6 | import android.content.Intent 7 | import android.content.pm.PackageManager 8 | import android.net.Uri 9 | import android.os.Build 10 | import android.os.Bundle 11 | import androidx.core.app.ActivityCompat 12 | import androidx.core.content.ContextCompat 13 | import androidx.appcompat.app.AppCompatActivity 14 | import android.util.Log 15 | import android.widget.Toast 16 | 17 | 18 | /** 19 | * Created by AndroidBuffer on 28/12/17. 20 | */ 21 | 22 | public class KotlinFilePicker : AppCompatActivity() { 23 | 24 | private val TAG = KotlinFilePicker::class.java.canonicalName 25 | private val REQUEST_MEDIA_CAPTURE = 101 26 | private val REQUEST_MEDIA_FILE = 102 27 | private val REQUEST_MEDIA_GALLERY = 103 28 | private val REQUEST_MEDIA_VIDEO = 104 29 | private val PERMISSION_REQUEST_STORAGE = 100 30 | private var intentPick: Intent? = null 31 | private var isPermissionDenied = false 32 | 33 | override fun onCreate(savedInstanceState: Bundle?) { 34 | super.onCreate(savedInstanceState) 35 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 36 | if (handlePermissionCheck()) { 37 | handleIntent(intent) 38 | } else { 39 | val permissions = arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE) 40 | ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_STORAGE) 41 | } 42 | } else { 43 | handleIntent(intent) 44 | } 45 | } 46 | 47 | override fun onStart() { 48 | super.onStart() 49 | if (isPermissionDenied) { 50 | KotUtil.openSettingsDialog(KotlinFilePicker@ this, true) 51 | } 52 | } 53 | 54 | private fun handlePermissionCheck(): Boolean { 55 | //check for the permission before accessing storage 56 | val permissionGranted = ContextCompat.checkSelfPermission(this, 57 | Manifest.permission.WRITE_EXTERNAL_STORAGE) 58 | if (permissionGranted == PackageManager.PERMISSION_GRANTED) { 59 | return true 60 | } 61 | return false 62 | } 63 | 64 | private fun handleIntent(intent: Intent) { 65 | //handle the intent passed from the client 66 | val selection = intent.getStringExtra(KotConstants.EXTRA_FILE_SELECTION) 67 | val isMultipleEnabled = intent.getBooleanExtra(KotConstants.EXTRA_MULTIPLE_ENABLED, false) 68 | when (selection) { 69 | KotConstants.SELECTION_TYPE_CAMERA -> { 70 | //camera intent 71 | val cameraIntent = KotUtil.getCameraIntent(this) 72 | if (cameraIntent == null) { 73 | throwException(getString(R.string.exception_msg_no_activity)) 74 | return 75 | } 76 | intentPick = cameraIntent 77 | startActivityForResult(cameraIntent, REQUEST_MEDIA_CAPTURE) 78 | } 79 | KotConstants.SELECTION_TYPE_GALLERY -> { 80 | //gallery intent 81 | val mimeType = if (intent.hasExtra(KotConstants.EXTRA_FILE_MIME_TYPE)) { 82 | intent.getStringExtra(KotConstants.EXTRA_FILE_MIME_TYPE) 83 | } else { 84 | KotConstants.FILE_TYPE_IMAGE_ALL 85 | } 86 | val galleryIntent = KotUtil.getGalleryIntent(mimeType, isMultipleEnabled) 87 | startActivityForResult(galleryIntent, REQUEST_MEDIA_GALLERY) 88 | } 89 | KotConstants.SELECTION_TYPE_FILE -> { 90 | //file intent 91 | val mimeType = if (intent.hasExtra(KotConstants.EXTRA_FILE_MIME_TYPE)) { 92 | intent.getStringExtra(KotConstants.EXTRA_FILE_MIME_TYPE) 93 | } else { 94 | KotConstants.FILE_TYPE_FILE_ALL 95 | } 96 | val fileIntent = KotUtil.getFileIntent(mimeType, isMultipleEnabled) 97 | startActivityForResult(fileIntent, REQUEST_MEDIA_FILE) 98 | } 99 | KotConstants.SELECTION_TYPE_VIDEO -> { 100 | val videoIntent = KotUtil.getVideoIntent(this) 101 | if (videoIntent == null) { 102 | throwException(getString(R.string.exception_msg_no_activity)) 103 | return 104 | } 105 | intentPick = videoIntent 106 | startActivityForResult(videoIntent, REQUEST_MEDIA_VIDEO) 107 | } 108 | else -> { 109 | throwException(getString(R.string.exception_msg_illegal_)) 110 | } 111 | } 112 | } 113 | 114 | private fun showToast(msg: String) { 115 | //show a toast 116 | Toast.makeText(this, msg, Toast.LENGTH_LONG).show() 117 | } 118 | 119 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { 120 | super.onRequestPermissionsResult(requestCode, permissions, grantResults) 121 | if (PERMISSION_REQUEST_STORAGE == requestCode) { 122 | for (permission in grantResults) { 123 | if (permission == PackageManager.PERMISSION_DENIED) { 124 | isPermissionDenied = true 125 | KotUtil.openSettingsDialog(KotlinFilePicker@ this, true) 126 | return 127 | } 128 | } 129 | handleIntent(intent) 130 | } 131 | } 132 | 133 | private fun throwException(msg: String) { 134 | //throws a exception in case of exception 135 | try { 136 | finish() 137 | throw IllegalArgumentException(msg) 138 | } catch (exp: IllegalArgumentException) { 139 | exp.printStackTrace() 140 | } 141 | } 142 | 143 | override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 144 | super.onActivityResult(requestCode, resultCode, data) 145 | if (REQUEST_MEDIA_CAPTURE == requestCode && resultCode == Activity.RESULT_OK) { 146 | //received the camera intent data 147 | val cameraUri = getUriList(intentPick) 148 | deliverResultSuccess(cameraUri) 149 | } else if (REQUEST_MEDIA_FILE == requestCode && resultCode == Activity.RESULT_OK) { 150 | //do something 151 | val fileUri = getUriList(data) 152 | deliverResultSuccess(fileUri) 153 | } else if (REQUEST_MEDIA_GALLERY == requestCode && resultCode == Activity.RESULT_OK) { 154 | //do something 155 | val galleryUri = getUriList(data) 156 | deliverResultSuccess(galleryUri) 157 | } else if (REQUEST_MEDIA_VIDEO == requestCode && resultCode == Activity.RESULT_OK) { 158 | //do something 159 | val videoUri = getUriList(intentPick) 160 | deliverResultSuccess(videoUri) 161 | } else { 162 | deliverResultFailed() 163 | } 164 | } 165 | 166 | private fun deliverResultSuccess(uri: ArrayList) { 167 | //returns the result back to calling activity 168 | val intent = Intent() 169 | intent.putParcelableArrayListExtra(KotConstants.EXTRA_FILE_RESULTS, 170 | getKotResultFromUri(this, uri)) 171 | setResult(Activity.RESULT_OK, intent) 172 | finish() 173 | } 174 | 175 | private fun getKotResultFromUri(context: Context, uri: ArrayList): ArrayList { 176 | //convert the uri to kotResult data class for file information 177 | val result = ArrayList() 178 | for (item in uri) { 179 | val file = KotUtil.getFileDetails(context, item!!) 180 | val fileSize = String.format("%1d KB", file!!.length() / 1024) 181 | val fileName = file.name 182 | val fileLocation = file.path 183 | val fileMimeType = KotUtil.getMimeType(fileLocation) 184 | val fileModified = KotUtil.getDateModified(file.lastModified()) 185 | val kotResult = KotResult(item, fileName, fileSize, fileLocation, fileMimeType, fileModified) 186 | result.add(kotResult) 187 | Log.d(TAG, fileLocation) 188 | } 189 | return result 190 | } 191 | 192 | private fun deliverResultFailed() { 193 | //marks the unsuccessful results delivery to parent activity 194 | setResult(Activity.RESULT_CANCELED, Intent()) 195 | finish() 196 | } 197 | 198 | override fun onBackPressed() { 199 | super.onBackPressed() 200 | deliverResultFailed() 201 | } 202 | 203 | private fun getUriList(intent: Intent?): ArrayList { 204 | //this returns a list of uri for passing back to parent intent 205 | val listUri = ArrayList() 206 | if (intent?.data == null) { 207 | //that means we may have data in clipdata 208 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 209 | val clipData = intent?.clipData 210 | (0 until clipData?.itemCount!!) 211 | .map { clipData.getItemAt(it) } 212 | .mapTo(listUri) { it.uri } 213 | } else { 214 | listUri.add(intent?.data) 215 | } 216 | } else { 217 | listUri.add(intent.data) 218 | } 219 | return listUri 220 | } 221 | 222 | } -------------------------------------------------------------------------------- /kotlinfilepicker/src/main/java/com/androidbuffer/kotlinfilepicker/KotRequest.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepicker 2 | 3 | import android.app.Activity 4 | import android.content.Intent 5 | 6 | /** 7 | * Created by AndroidBuffer on 11/1/18. 8 | */ 9 | public class KotRequest { 10 | 11 | /** 12 | * inner class for building the camera request 13 | */ 14 | class Camera(context: Activity) { 15 | 16 | var requestCode = 101 17 | var intent: Intent 18 | var activity: Activity 19 | 20 | init { 21 | this.activity = context 22 | intent = Intent(context, KotlinFilePicker::class.java) 23 | intent.putExtra(KotConstants.EXTRA_FILE_SELECTION, KotConstants.SELECTION_TYPE_CAMERA) 24 | } 25 | 26 | /** 27 | * secondary constructor pass for initialization of camera class 28 | * @param requestCode by default it is 101 give a value to change it 29 | * @param context 30 | */ 31 | constructor(context: Activity, requestCode: Int = 101) : this(context) { 32 | this.activity = context 33 | this.requestCode = requestCode 34 | } 35 | 36 | /** 37 | * set the request code & returns object of Camera class for launching the camera intent 38 | * @param requestCode for tracking in onActivityResult(...) 39 | * @return {@link Camera} 40 | */ 41 | fun setRequestCode(requestCode: Int): Camera { 42 | this.requestCode = requestCode 43 | return this 44 | } 45 | 46 | /** 47 | * returns a intent for camera if initialized using constructor 48 | */ 49 | fun getCameraIntent(): Intent { 50 | return intent 51 | } 52 | 53 | /** 54 | * starts intent to capture image from camera KotlinFilePicker.class 55 | */ 56 | fun pick() { 57 | activity.startActivityForResult(intent, requestCode) 58 | } 59 | } 60 | 61 | /** 62 | * inner class for starting recording video 63 | */ 64 | class Video(context: Activity) { 65 | private var activity: Activity 66 | private var requestCode = 102; 67 | private var intent: Intent 68 | 69 | init { 70 | this.activity = context 71 | intent = Intent(activity, KotlinFilePicker::class.java) 72 | intent.putExtra(KotConstants.EXTRA_FILE_SELECTION, KotConstants.SELECTION_TYPE_VIDEO) 73 | } 74 | 75 | /** 76 | * secondary constructor for changing the request code 77 | */ 78 | constructor(context: Activity, requestCode: Int) : this(context) { 79 | this.activity = context 80 | this.requestCode = requestCode 81 | } 82 | 83 | /** 84 | * set the request code if missed in constructor initialization 85 | */ 86 | fun setRequestCode(requestCode: Int): Video { 87 | this.requestCode = requestCode 88 | return this 89 | } 90 | 91 | /** 92 | * set the mime type of the file selection, 93 | * please note by default value is @see KotConstants FILE_TYPE_VIDEO_ALL 94 | * setting mime type will override the default one 95 | * @param mimeType 96 | */ 97 | fun setMimeType(mimeType: String): Video { 98 | intent.putExtra(KotConstants.EXTRA_FILE_MIME_TYPE, mimeType) 99 | return this 100 | } 101 | 102 | /** 103 | * if set and initialized through constructor then we can get intent back, 104 | * use pick() instead if intent is not required 105 | * @return Intent 106 | */ 107 | fun getVideoIntent(): Intent { 108 | return intent 109 | } 110 | 111 | /** 112 | * call this method after initialization in last 113 | */ 114 | fun pick() { 115 | activity.startActivityForResult(intent, requestCode) 116 | } 117 | } 118 | 119 | /** 120 | * inner class for picking images from gallery 121 | */ 122 | class Gallery(context: Activity) { 123 | private var activity: Activity 124 | private var requestCode = 103; 125 | private var intent: Intent 126 | 127 | init { 128 | this.activity = context 129 | intent = Intent(activity, KotlinFilePicker::class.java) 130 | intent.putExtra(KotConstants.EXTRA_FILE_SELECTION, KotConstants.SELECTION_TYPE_GALLERY) 131 | putMultiple(false) 132 | } 133 | 134 | /** 135 | * for selection single or multiple 136 | * @param multipleEnabled 137 | */ 138 | private fun putMultiple(multipleEnabled: Boolean) { 139 | intent.putExtra(KotConstants.EXTRA_MULTIPLE_ENABLED, multipleEnabled) 140 | } 141 | 142 | /** 143 | * secondary constructor for changing the request code 144 | * @constructor pass activity and request code 145 | */ 146 | constructor(context: Activity, requestCode: Int) : this(context) { 147 | this.activity = context 148 | this.requestCode = requestCode 149 | } 150 | 151 | /** 152 | * set the request code if missed in constructor initialization 153 | * @param requestCode 154 | * @return Gallery 155 | */ 156 | fun setRequestCode(requestCode: Int): Gallery { 157 | this.requestCode = requestCode 158 | return this 159 | } 160 | 161 | /** 162 | * set the type selection single or multiple 163 | * @param isMultipleEnabled 164 | * @return Gallery 165 | */ 166 | fun isMultiple(isMultipleEnabled: Boolean): Gallery { 167 | putMultiple(isMultipleEnabled) 168 | return this 169 | } 170 | 171 | /** 172 | * set the mime type of the file selection, 173 | * please note by default value is @see KotConstants FILE_TYPE_IMAGE_ALL 174 | * setting mime type will override the default one 175 | * @param mimeType 176 | */ 177 | fun setMimeType(mimeType: String): Gallery { 178 | intent.putExtra(KotConstants.EXTRA_FILE_MIME_TYPE, mimeType) 179 | return this 180 | } 181 | 182 | /** 183 | * if set and initialized through constructor then we can get intent back 184 | * use pick() instead if intent is not required 185 | * @return @see Intent 186 | */ 187 | fun getGalleryIntent(): Intent { 188 | return intent 189 | } 190 | 191 | /** 192 | * By default multiple selection is false @see multipleEnabled() 193 | * call this method after initialization in last 194 | */ 195 | fun pick() { 196 | activity.startActivityForResult(intent, requestCode) 197 | } 198 | } 199 | 200 | 201 | class File(context: Activity) { 202 | private var activity: Activity 203 | private var requestCode = 104; 204 | private var intent: Intent 205 | 206 | init { 207 | this.activity = context 208 | intent = Intent(activity, KotlinFilePicker::class.java) 209 | intent.putExtra(KotConstants.EXTRA_FILE_SELECTION, KotConstants.SELECTION_TYPE_FILE) 210 | putMultiple(false) 211 | } 212 | 213 | /** 214 | * for selection single or multiple 215 | * @param multipleEnabled 216 | */ 217 | private fun putMultiple(multipleEnabled: Boolean) { 218 | intent.putExtra(KotConstants.EXTRA_MULTIPLE_ENABLED, multipleEnabled) 219 | } 220 | 221 | /** 222 | * secondary constructor for changing the request code 223 | * @constructor pass activity and request code 224 | */ 225 | constructor(context: Activity, requestCode: Int) : this(context) { 226 | this.activity = context 227 | this.requestCode = requestCode 228 | } 229 | 230 | /** 231 | * set the request code if missed in constructor initialization 232 | * @param requestCode 233 | * @return Gallery 234 | */ 235 | fun setRequestCode(requestCode: Int): File { 236 | this.requestCode = requestCode 237 | return this 238 | } 239 | 240 | /** 241 | * set the type selection single or multiple 242 | * @param isMultipleEnabled 243 | * @return Gallery 244 | */ 245 | fun isMultiple(isMultipleEnabled: Boolean): File { 246 | putMultiple(isMultipleEnabled) 247 | return this 248 | } 249 | 250 | /** 251 | * set the mime type of the file selection, 252 | * please note by default value is @see KotConstants FILE_TYPE_FILE_ALL 253 | * setting mime type will override the default one 254 | * @param mimeType 255 | */ 256 | fun setMimeType(mimeType: String): File { 257 | intent.putExtra(KotConstants.EXTRA_FILE_MIME_TYPE, mimeType) 258 | return this 259 | } 260 | 261 | /** 262 | * if set and initialized through constructor then we can get intent back 263 | * use pick() instead if intent is not required 264 | * @return @see Intent 265 | */ 266 | fun getFileIntent(): Intent { 267 | return intent 268 | } 269 | 270 | /** 271 | * By default multiple selection is false @see multipleEnabled() 272 | * call this method after initialization in last 273 | */ 274 | fun pick() { 275 | activity.startActivityForResult(intent, requestCode) 276 | } 277 | } 278 | 279 | 280 | } -------------------------------------------------------------------------------- /kotlinfilepicker/src/main/java/com/androidbuffer/kotlinfilepicker/KotUtil.kt: -------------------------------------------------------------------------------- 1 | package com.androidbuffer.kotlinfilepicker 2 | 3 | import android.app.Activity 4 | import android.content.* 5 | import android.database.Cursor 6 | import android.database.CursorIndexOutOfBoundsException 7 | import android.net.Uri 8 | import android.os.Build 9 | import android.os.Environment 10 | import android.provider.DocumentsContract 11 | import android.provider.MediaStore 12 | import android.provider.Settings 13 | import androidx.core.content.FileProvider 14 | import androidx.appcompat.app.AlertDialog 15 | import android.text.TextUtils 16 | import android.webkit.MimeTypeMap 17 | import java.io.File 18 | import java.text.SimpleDateFormat 19 | import java.util.* 20 | import java.util.regex.Pattern 21 | 22 | 23 | /** 24 | * Created by AndroidBuffer on 3/1/18. 25 | */ 26 | class KotUtil { 27 | 28 | companion object { 29 | 30 | private fun authority(context: Context) = context.packageName + ".fileprovider" 31 | 32 | /** 33 | * @return {@link Intent} 34 | * @param context 35 | * returns a intent for camera 36 | */ 37 | fun getCameraIntent(context: Context): Intent? { 38 | //returns a camera intent with temp file location 39 | val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) 40 | if (cameraIntent.resolveActivity(context.packageManager) == null) { 41 | return null 42 | } 43 | val file = createImageFile(context) 44 | if (file != null) { 45 | val uri = getUriFromFile(context, file) 46 | grantUriPermission(context, cameraIntent, uri) 47 | cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri) 48 | } 49 | cameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) 50 | return cameraIntent 51 | } 52 | 53 | /** 54 | * @return {@link Intent} 55 | * @param context 56 | * returns a intent for video 57 | */ 58 | fun getVideoIntent(context: Context): Intent? { 59 | //returns a video recording intent with temp file location 60 | val videoIntent = Intent(MediaStore.ACTION_VIDEO_CAPTURE) 61 | if (videoIntent.resolveActivity(context.packageManager) == null) { 62 | return null 63 | } 64 | val file = createVideoFile(context) 65 | if (file != null) { 66 | val uri = getUriFromFile(context, file) 67 | grantUriPermission(context, videoIntent, uri) 68 | videoIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri) 69 | } 70 | videoIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 71 | return videoIntent 72 | } 73 | 74 | /** 75 | * @return {@link Intent} 76 | * @param isMultiple 77 | * @param mimeType 78 | * multiple select works for only API level 18 and above 79 | */ 80 | fun getGalleryIntent(mimeType: String, isMultiple: Boolean): Intent { 81 | val intent: Intent 82 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 83 | intent = Intent(Intent.ACTION_OPEN_DOCUMENT) 84 | intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) 85 | } else { 86 | intent = Intent(Intent.ACTION_GET_CONTENT) 87 | } 88 | intent.type = mimeType 89 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 90 | intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, isMultiple) 91 | } 92 | intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 93 | intent.addCategory(Intent.CATEGORY_OPENABLE) 94 | return intent 95 | } 96 | 97 | /** 98 | * @return {@link Intent} 99 | * @param mimeType 100 | * returns a intent for file 101 | */ 102 | fun getFileIntent(mimeType: String, isMultiple: Boolean): Intent { 103 | val intent: Intent 104 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { 105 | intent = Intent(Intent.ACTION_OPEN_DOCUMENT) 106 | intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) 107 | } else { 108 | intent = Intent(Intent.ACTION_GET_CONTENT) 109 | } 110 | intent.type = mimeType 111 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 112 | intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, isMultiple) 113 | } 114 | intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) 115 | intent.addCategory(Intent.CATEGORY_OPENABLE) 116 | return intent 117 | } 118 | 119 | private fun createImageFile(context: Context): File? { 120 | //this returns a temp file object 121 | val fileName = "image_" + createFileName() 122 | val file = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) 123 | return if (file == null) return null else File.createTempFile(fileName, ".jpg", file) 124 | } 125 | 126 | private fun createVideoFile(context: Context): File? { 127 | //this returns a temp file object 128 | val fileName = "video" + createFileName() 129 | val file = context.getExternalFilesDir(Environment.DIRECTORY_MOVIES) 130 | return if (file == null) return null else File.createTempFile(fileName, ".mp4", file) 131 | } 132 | 133 | private fun createFileName(): String { 134 | //this returns a string file name 135 | return SimpleDateFormat("ddMMyyyy_HHmmssSS", Locale.getDefault()).format(Date()) 136 | } 137 | 138 | private fun getUriFromFile(context: Context, file: File): Uri { 139 | //returns uri from file object 140 | return FileProvider.getUriForFile(context, authority(context), file) 141 | } 142 | 143 | private fun grantUriPermission(context: Context, intent: Intent, uri: Uri) { 144 | //grant the uri permission to all the api versions 145 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 146 | intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION) 147 | } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 148 | val clip = ClipData.newUri(context.contentResolver, "camera", uri) 149 | intent.clipData = clip 150 | intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION or Intent.FLAG_GRANT_READ_URI_PERMISSION) 151 | } 152 | } 153 | 154 | fun getFileDetails(context: Context, uri: Uri): File? { 155 | //get the details from uri 156 | var fileToReturn: File? = null 157 | try { 158 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { 159 | val tables = arrayOf(MediaStore.Images.Media.DATA) 160 | val cursorLoader = CursorLoader(context, uri, tables, null, null, null) 161 | val cursor = cursorLoader.loadInBackground() 162 | val columnIndex = cursor.getColumnIndex(MediaStore.Images.Media.DATA) 163 | 164 | if (cursor.moveToNext()) { 165 | val result = cursor.getString(columnIndex) 166 | fileToReturn = File(result) 167 | } 168 | cursor.close() 169 | 170 | } else { 171 | fileToReturn = File(readPathFromUri(context, uri)) 172 | } 173 | } catch (exp: CursorIndexOutOfBoundsException) { 174 | exp.printStackTrace() 175 | fileToReturn = File(uri.path) 176 | } catch (exp: NullPointerException) { 177 | exp.printStackTrace() 178 | fileToReturn = File(uri.path) 179 | } catch (exp: NumberFormatException) { 180 | exp.printStackTrace() 181 | fileToReturn = File(uri.path) 182 | } 183 | return fileToReturn 184 | } 185 | 186 | /** 187 | * get the extension from path of the file 188 | * @param url 189 | */ 190 | fun getMimeType(url: String): String { 191 | val extension = getFileExtensionFromUrl(url) 192 | if (!extension.isEmpty()) { 193 | val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension.toLowerCase()) 194 | if (type.isNullOrBlank()) { 195 | return "*/*" 196 | } else { 197 | return type 198 | } 199 | } else { 200 | return "*/*" 201 | } 202 | } 203 | 204 | /** 205 | * get the date in format dd/MM/yyyy from long date 206 | * @param date 207 | * @return string date 208 | */ 209 | fun getDateModified(date: Long): String { 210 | val simpleDate = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) 211 | return simpleDate.format(date) 212 | } 213 | 214 | /** 215 | * get a file extension from the file path 216 | * @param url 217 | */ 218 | fun getFileExtensionFromUrl(passedUrl: String): String { 219 | var url = passedUrl 220 | if (!TextUtils.isEmpty(url)) { 221 | val fragment = url.lastIndexOf('#') 222 | if (fragment > 0) { 223 | url = url.substring(0, fragment) 224 | } 225 | 226 | val query = url.lastIndexOf('?') 227 | if (query > 0) { 228 | url = url.substring(0, query) 229 | } 230 | 231 | val filenamePos = url.lastIndexOf('/') 232 | val filename = if (0 <= filenamePos) url.substring(filenamePos + 1) else url 233 | 234 | // if the filename contains special characters, we don't 235 | // consider it valid for our matching purposes except space: 236 | if (!filename.isEmpty() && Pattern.matches("[\\sa-zA-Z_0-9\\.\\-\\(\\)\\%]+", filename)) { 237 | val dotPos = filename.lastIndexOf('.') 238 | if (0 <= dotPos) { 239 | return filename.substring(dotPos + 1) 240 | } 241 | } 242 | } 243 | 244 | return "" 245 | } 246 | 247 | /** 248 | * returns the path from uri for API level 19 and up 249 | */ 250 | @SuppressWarnings("NewApi") 251 | private fun readPathFromUri(context: Context, uri: Uri): String? { 252 | 253 | // DocumentProvider 254 | if (DocumentsContract.isDocumentUri(context, uri)) { 255 | // ExternalStorageProvider 256 | if (isExternalStorageDocument(uri)) { 257 | val docId = DocumentsContract.getDocumentId(uri) 258 | val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() 259 | val type = split[0] 260 | 261 | if ("primary".equals(type, ignoreCase = true)) { 262 | return Environment.getExternalStorageDirectory().toString() + "/" + split[1] 263 | } 264 | } else if (isDownloadsDocument(uri)) { 265 | val id = DocumentsContract.getDocumentId(uri) 266 | val contentUri = ContentUris.withAppendedId( 267 | Uri.parse("content://downloads/public_downloads"), java.lang.Long.valueOf(id)!!) 268 | 269 | return getDataColumn(context, contentUri, null, null) 270 | } else if (isMediaDocument(uri)) { 271 | val docId = DocumentsContract.getDocumentId(uri) 272 | val split = docId.split(":".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray() 273 | val type = split[0] 274 | 275 | val contentUri = getContentUri(type) 276 | 277 | val selection = "_id=?" 278 | val selectionArgs = arrayOf(split[1]) 279 | return getDataColumn(context, contentUri, selection, selectionArgs) 280 | }// MediaProvider 281 | // DownloadsProvider 282 | } else if ("content".equals(uri.scheme, ignoreCase = true)) { 283 | // Return the remote address 284 | return if (isGooglePhotosUri(uri)) { 285 | uri.lastPathSegment 286 | } else getDataColumn(context, uri, null, null) 287 | } else if ("file".equals(uri.scheme, ignoreCase = true)) { 288 | return uri.path 289 | } 290 | 291 | return null 292 | } 293 | 294 | /** 295 | * checks if the given uri is of type external storage 296 | */ 297 | private fun isExternalStorageDocument(uri: Uri): Boolean { 298 | return "com.android.externalstorage.documents" == uri.authority 299 | } 300 | 301 | /** 302 | * checks if the given uri is of type downloads 303 | */ 304 | private fun isDownloadsDocument(uri: Uri): Boolean { 305 | return "com.android.providers.downloads.documents" == uri.authority 306 | } 307 | 308 | /** 309 | * checks if the given uri is of type media document 310 | */ 311 | private fun isMediaDocument(uri: Uri): Boolean { 312 | return "com.android.providers.media.documents" == uri.authority 313 | } 314 | 315 | /** 316 | * returns the column for specified uri 317 | */ 318 | private fun getDataColumn(context: Context, uri: Uri?, selection: String?, selectionArgs: Array?): String? { 319 | var cursor: Cursor? = null 320 | val column = MediaStore.Images.Media.DATA 321 | val projection = arrayOf(column) 322 | 323 | try { 324 | cursor = context.contentResolver.query(uri, projection, selection, selectionArgs, null) 325 | if (cursor != null && cursor!!.moveToFirst()) { 326 | val column_index = cursor!!.getColumnIndexOrThrow(column) 327 | return cursor!!.getString(column_index) 328 | } 329 | } catch (e: IllegalArgumentException) { 330 | e.printStackTrace() 331 | } finally { 332 | if (cursor != null) { 333 | cursor!!.close() 334 | } 335 | } 336 | return null 337 | } 338 | 339 | /** 340 | * checks the type of uri 341 | */ 342 | private fun getContentUri(type: String): Uri? { 343 | when (type) { 344 | "image" -> return MediaStore.Images.Media.EXTERNAL_CONTENT_URI 345 | "video" -> return MediaStore.Video.Media.EXTERNAL_CONTENT_URI 346 | "audio" -> return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI 347 | } 348 | return null 349 | } 350 | 351 | /** 352 | * checks if the uri is of type google photos 353 | */ 354 | private fun isGooglePhotosUri(uri: Uri): Boolean { 355 | return "com.google.android.apps.photos.content" == uri.authority 356 | } 357 | 358 | /** 359 | * opens the settings activity for app 360 | * @param activity 361 | * @param 362 | */ 363 | fun openSettingsDialog(activity: Activity, wantToFinishOnOk: Boolean) { 364 | val alertBuilder = AlertDialog.Builder(activity) 365 | 366 | alertBuilder.setPositiveButton(R.string.dialog_settings_button, { dialogInterface, i -> 367 | val intent = Intent() 368 | intent.action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS 369 | val uri = Uri.fromParts("package", activity.packageName, null) 370 | intent.data = uri 371 | activity.startActivity(intent) 372 | }) 373 | alertBuilder.setNegativeButton(R.string.dialog_finish_button, { dialogInterface, i -> 374 | dialogInterface.dismiss() 375 | if (wantToFinishOnOk) { 376 | activity.finish() 377 | } 378 | }) 379 | alertBuilder.setMessage(R.string.dialog_permissions_message) 380 | alertBuilder.setCancelable(false) 381 | alertBuilder.create().show() 382 | } 383 | 384 | } 385 | 386 | } --------------------------------------------------------------------------------