diff --git a/.gitignore b/.gitignore
index 4a264a28d8..ecfdcb7510 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,137 @@
/tmp
ktlint
+multipicker/build/.transforms/3e0e90edbb388b6989e862c9faf75e87.bin
+multipicker/build/.transforms/55ef863ef687bb455ca3376fbf042ba5.bin
+multipicker/build/.transforms/3e0e90edbb388b6989e862c9faf75e87/classes/classes.dex
+multipicker/build/.transforms/55ef863ef687bb455ca3376fbf042ba5/classes/classes.dex
+multipicker/build/generated/source/buildConfig/debug/im/vector/riotx/multipicker/BuildConfig.java
+multipicker/build/intermediates/aapt_friendly_merged_manifests/debug/aapt/AndroidManifest.xml
+multipicker/build/intermediates/aapt_friendly_merged_manifests/debug/aapt/output.json
+multipicker/build/intermediates/annotation_processor_list/debug/annotationProcessors.json
+multipicker/build/intermediates/annotations_typedef_file/debug/extractDebugAnnotations/typedefs.txt
+multipicker/build/intermediates/compile_library_classes/debug/classes.jar
+multipicker/build/intermediates/compile_only_not_namespaced_r_class_jar/debug/R.jar
+multipicker/build/intermediates/consumer_proguard_file/debug/proguard.txt
+multipicker/build/intermediates/incremental/debug-mergeJavaRes/merge-state
+multipicker/build/intermediates/incremental/debug-mergeNativeLibs/merge-state
+multipicker/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml
+multipicker/build/intermediates/incremental/mergeDebugShaders/merger.xml
+multipicker/build/intermediates/incremental/packageDebugAssets/merger.xml
+multipicker/build/intermediates/incremental/packageDebugResources/compile-file-map.properties
+multipicker/build/intermediates/incremental/packageDebugResources/merger.xml
+multipicker/build/intermediates/javac/debug/classes/im/vector/riotx/multipicker/BuildConfig.class
+multipicker/build/intermediates/library_java_res/debug/res.jar
+multipicker/build/intermediates/library_manifest/debug/AndroidManifest.xml
+multipicker/build/intermediates/local_only_symbol_list/debug/parseDebugLibraryResources/R-def.txt
+multipicker/build/intermediates/manifest_merge_blame_file/debug/manifest-merger-blame-debug-report.txt
+multipicker/build/intermediates/merged_java_res/debug/out.jar
+multipicker/build/intermediates/merged_manifests/debug/output.json
+multipicker/build/intermediates/packaged-classes/debug/classes.jar
+multipicker/build/intermediates/res/symbol-table-with-package/debug/package-aware-r.txt
+multipicker/build/intermediates/runtime_library_classes/debug/classes.jar
+multipicker/build/intermediates/symbols/debug/R.txt
+multipicker/build/kotlin/compileDebugKotlin/build-history.bin
+multipicker/build/kotlin/compileDebugKotlin/last-build.bin
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/inputs/source-to-output.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/inputs/source-to-output.tab.keystream
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/inputs/source-to-output.tab.keystream.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/inputs/source-to-output.tab.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/inputs/source-to-output.tab.values.at
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/inputs/source-to-output.tab_i
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/inputs/source-to-output.tab_i.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/constants.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/constants.tab.keystream
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/constants.tab.keystream.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/constants.tab.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/constants.tab.values.at
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/constants.tab_i
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/constants.tab_i.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/proto.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/proto.tab.keystream
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/proto.tab.keystream.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/proto.tab.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/proto.tab.values.at
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/proto.tab_i
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/proto.tab_i.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/source-to-classes.tab_i.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/subtypes.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/subtypes.tab.keystream
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/subtypes.tab.keystream.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/subtypes.tab.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/subtypes.tab.values.at
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/subtypes.tab_i
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/subtypes.tab_i.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/supertypes.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/supertypes.tab.keystream
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/supertypes.tab.keystream.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/supertypes.tab.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/supertypes.tab.values.at
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/supertypes.tab_i
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/jvm/kotlin/supertypes.tab_i.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/counters.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/file-to-id.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/file-to-id.tab.keystream
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/file-to-id.tab.keystream.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/file-to-id.tab.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/file-to-id.tab.values.at
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/file-to-id.tab_i
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/file-to-id.tab_i.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/id-to-file.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/id-to-file.tab.keystream
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/id-to-file.tab.keystream.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/id-to-file.tab.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/id-to-file.tab.values.at
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/id-to-file.tab_i
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/id-to-file.tab_i.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/lookups.tab
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/lookups.tab.keystream
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/lookups.tab.keystream.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/lookups.tab.len
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/lookups.tab.values
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/lookups.tab.values.at
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/lookups.tab.values.s
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/lookups.tab_i
+multipicker/build/kotlin/compileDebugKotlin/caches-jvm/lookups/lookups.tab_i.len
+multipicker/build/outputs/aar/multipicker-debug.aar
+multipicker/build/outputs/logs/manifest-merger-debug-report.txt
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/AudioPicker.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/ContactPicker.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/FilePicker.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/ImagePicker.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/MultiPicker$Type$AUDIO$2.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/MultiPicker$Type$CONTACT$2.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/MultiPicker$Type$FILE$2.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/MultiPicker$Type$IMAGE$2.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/MultiPicker$Type$VIDEO$2.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/MultiPicker$Type.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/MultiPicker.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/Picker.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/VideoPicker.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/entity/MultiPickerAudioType.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/entity/MultiPickerBaseType.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/entity/MultiPickerContactType.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/entity/MultiPickerFileType.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/entity/MultiPickerImageType.class
+multipicker/build/tmp/kotlin-classes/debug/im/vector/riotx/multipicker/entity/MultiPickerVideoType.class
+multipicker/build/tmp/kotlin-classes/debug/META-INF/multipicker_debug.kotlin_module
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index 5f614763d5..ca6c2ce6d9 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -119,7 +119,7 @@ dependencies {
implementation "ru.noties.markwon:core:$markwon_version"
// Image
- implementation 'androidx.exifinterface:exifinterface:1.1.0'
+ implementation 'androidx.exifinterface:exifinterface:1.3.0-alpha01'
implementation 'id.zelory:compressor:3.0.0'
// Database
diff --git a/multipicker/.gitignore b/multipicker/.gitignore
new file mode 100644
index 0000000000..796b96d1c4
--- /dev/null
+++ b/multipicker/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/multipicker/build.gradle b/multipicker/build.gradle
new file mode 100644
index 0000000000..950e76020f
--- /dev/null
+++ b/multipicker/build.gradle
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.3"
+
+ defaultConfig {
+ minSdkVersion 19
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ consumerProguardFiles 'consumer-rules.pro'
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+ implementation 'androidx.core:core-ktx:1.2.0'
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.1'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+
+ implementation 'androidx.exifinterface:exifinterface:1.3.0-alpha01'
+}
diff --git a/multipicker/consumer-rules.pro b/multipicker/consumer-rules.pro
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/multipicker/proguard-rules.pro b/multipicker/proguard-rules.pro
new file mode 100644
index 0000000000..f1b424510d
--- /dev/null
+++ b/multipicker/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# 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
diff --git a/multipicker/src/androidTest/java/im/vector/riotx/multipicker/ExampleInstrumentedTest.kt b/multipicker/src/androidTest/java/im/vector/riotx/multipicker/ExampleInstrumentedTest.kt
new file mode 100644
index 0000000000..25bf17559f
--- /dev/null
+++ b/multipicker/src/androidTest/java/im/vector/riotx/multipicker/ExampleInstrumentedTest.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker
+
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import junit.framework.Assert.assertEquals
+
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@RunWith(AndroidJUnit4::class)
+class ExampleInstrumentedTest {
+ @Test
+ fun useAppContext() {
+ // Context of the app under test.
+ val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+ assertEquals("im.vector.riotx.multipicker.test", appContext.packageName)
+ }
+}
diff --git a/multipicker/src/main/AndroidManifest.xml b/multipicker/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..068a7c9a19
--- /dev/null
+++ b/multipicker/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/AudioPicker.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/AudioPicker.kt
new file mode 100644
index 0000000000..19aba7bf1a
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/AudioPicker.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.media.MediaMetadataRetriever
+import android.net.Uri
+import android.provider.MediaStore
+import androidx.fragment.app.Fragment
+import im.vector.riotx.multipicker.entity.MultiPickerAudioType
+
+class AudioPicker(override val requestCode: Int) : Picker(requestCode) {
+
+ override fun startWith(activity: Activity) {
+ activity.startActivityForResult(createIntent(), requestCode)
+ }
+
+ override fun startWith(fragment: Fragment) {
+ fragment.startActivityForResult(createIntent(), requestCode)
+ }
+
+ override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List {
+ if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) {
+ return emptyList()
+ }
+
+ val audioList = mutableListOf()
+
+ val selectedUriList = mutableListOf()
+ val dataUri = data?.data
+ val clipData = data?.clipData
+
+ if (clipData != null) {
+ for (i in 0 until clipData.itemCount) {
+ selectedUriList.add(clipData.getItemAt(i).uri)
+ }
+ } else if (dataUri != null) {
+ selectedUriList.add(dataUri)
+ }
+
+ selectedUriList.forEach { selectedUri ->
+ val projection = arrayOf(
+ MediaStore.Audio.Media.DISPLAY_NAME,
+ MediaStore.Audio.Media.SIZE
+ )
+
+ context.contentResolver.query(
+ selectedUri,
+ projection,
+ null,
+ null,
+ null
+ )?.use { cursor ->
+ val nameColumn = cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)
+ val sizeColumn = cursor.getColumnIndex(MediaStore.Audio.Media.SIZE)
+
+ if (cursor.moveToNext()) {
+ val name = cursor.getString(nameColumn)
+ val size = cursor.getLong(sizeColumn)
+ var duration = 0L
+
+ context.contentResolver.openFileDescriptor(selectedUri, "r")?.use { pfd ->
+ val mediaMetadataRetriever = MediaMetadataRetriever()
+ mediaMetadataRetriever.setDataSource(pfd.fileDescriptor)
+ duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toLong()
+ }
+
+ audioList.add(
+ MultiPickerAudioType(
+ name,
+ size,
+ context.contentResolver.getType(selectedUri),
+ selectedUri,
+ duration
+ )
+ )
+ }
+ }
+ }
+ return audioList
+ }
+
+ private fun createIntent(): Intent {
+ return Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ putExtra(Intent.EXTRA_ALLOW_MULTIPLE, !single)
+ type = "audio/*"
+ }
+ }
+}
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/ContactPicker.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/ContactPicker.kt
new file mode 100644
index 0000000000..aebde6f439
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/ContactPicker.kt
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker
+
+import android.app.Activity
+import android.content.ContentResolver
+import android.content.Context
+import android.content.Intent
+import android.provider.ContactsContract
+import androidx.fragment.app.Fragment
+import im.vector.riotx.multipicker.entity.MultiPickerContactType
+
+class ContactPicker(override val requestCode: Int) : Picker(requestCode) {
+
+ override fun startWith(activity: Activity) {
+ activity.startActivityForResult(createIntent(), requestCode)
+ }
+
+ override fun startWith(fragment: Fragment) {
+ fragment.startActivityForResult(createIntent(), requestCode)
+ }
+
+ override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List {
+ if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) {
+ return emptyList()
+ }
+
+ val contactList = mutableListOf()
+
+ data?.data?.let { selectedUri ->
+ val projection = arrayOf(
+ ContactsContract.Contacts.DISPLAY_NAME,
+ ContactsContract.Contacts.PHOTO_URI,
+ ContactsContract.Contacts._ID
+ )
+
+ context.contentResolver.query(
+ selectedUri,
+ projection,
+ null,
+ null,
+ null
+ )?.use { cursor ->
+ if (cursor.moveToFirst()) {
+ val idColumn = cursor.getColumnIndex(ContactsContract.Contacts._ID)
+ val nameColumn = cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME)
+ val photoUriColumn = cursor.getColumnIndex(ContactsContract.Contacts.PHOTO_URI)
+
+ val contactId = cursor.getInt(idColumn)
+ var name = cursor.getString(nameColumn)
+ var photoUri = cursor.getString(photoUriColumn)
+ var phoneNumberList = mutableListOf()
+ var emailList = mutableListOf()
+
+ getRawContactId(context.contentResolver, contactId)?.let { rawContactId ->
+ val selection = ContactsContract.Data.RAW_CONTACT_ID + " = ?"
+ val selectionArgs = arrayOf(rawContactId.toString())
+
+ context.contentResolver.query(
+ ContactsContract.Data.CONTENT_URI,
+ arrayOf(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.Data.DATA1
+ ),
+ selection,
+ selectionArgs,
+ null
+ )?.use { cursor ->
+ while (cursor.moveToNext()) {
+ val mimeType = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.MIMETYPE))
+ val contactData = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.DATA1))
+
+ if (mimeType == ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE) {
+ name = contactData
+ }
+ if (mimeType == ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE) {
+ phoneNumberList.add(contactData)
+ }
+ if (mimeType == ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE) {
+ emailList.add(contactData)
+ }
+ }
+ }
+ }
+ contactList.add(
+ MultiPickerContactType(
+ name,
+ photoUri,
+ phoneNumberList,
+ emailList
+ )
+ )
+ }
+ }
+ }
+
+ return contactList
+ }
+
+ private fun getRawContactId(contentResolver: ContentResolver, contactId: Int): Int? {
+ val projection = arrayOf(ContactsContract.RawContacts._ID)
+ val selection = ContactsContract.RawContacts.CONTACT_ID + " = ?"
+ val selectionArgs = arrayOf(contactId.toString() + "")
+ return contentResolver.query(
+ ContactsContract.RawContacts.CONTENT_URI,
+ projection,
+ selection,
+ selectionArgs,
+ null
+ )?.use { cursor ->
+ return if (cursor.moveToFirst()) cursor.getInt(cursor.getColumnIndex(ContactsContract.RawContacts._ID)) else null
+ }
+ }
+
+ private fun createIntent(): Intent {
+ return Intent(Intent.ACTION_PICK).apply {
+ putExtra(Intent.EXTRA_ALLOW_MULTIPLE, !single)
+ type = ContactsContract.Contacts.CONTENT_TYPE
+ }
+ }
+}
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/FilePicker.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/FilePicker.kt
new file mode 100644
index 0000000000..cf6b74c62d
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/FilePicker.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.net.Uri
+import android.provider.OpenableColumns
+import androidx.fragment.app.Fragment
+import im.vector.riotx.multipicker.entity.MultiPickerFileType
+
+class FilePicker(override val requestCode: Int) : Picker(requestCode) {
+
+ override fun startWith(activity: Activity) {
+ activity.startActivityForResult(createIntent(), requestCode)
+ }
+
+ override fun startWith(fragment: Fragment) {
+ fragment.startActivityForResult(createIntent(), requestCode)
+ }
+
+ override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List {
+ if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) {
+ return emptyList()
+ }
+
+ val fileList = mutableListOf()
+
+ val selectedUriList = mutableListOf()
+ val dataUri = data?.data
+ val clipData = data?.clipData
+
+ if (clipData != null) {
+ for (i in 0 until clipData.itemCount) {
+ selectedUriList.add(clipData.getItemAt(i).uri)
+ }
+ } else if (dataUri != null) {
+ selectedUriList.add(dataUri)
+ }
+
+ selectedUriList.forEach { selectedUri ->
+ context.contentResolver.query(selectedUri, null, null, null, null)
+ ?.use { cursor ->
+ val nameColumn = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
+ val sizeColumn = cursor.getColumnIndex(OpenableColumns.SIZE)
+ if (cursor.moveToFirst()) {
+ val name = cursor.getString(nameColumn)
+ val size = cursor.getLong(sizeColumn)
+
+ fileList.add(
+ MultiPickerFileType(
+ name,
+ size,
+ context.contentResolver.getType(selectedUri),
+ selectedUri
+ )
+ )
+ }
+ }
+ }
+ return fileList
+ }
+
+ private fun createIntent(): Intent {
+ return Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ putExtra(Intent.EXTRA_ALLOW_MULTIPLE, !single)
+ type = "*/*"
+ }
+ }
+}
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/ImagePicker.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/ImagePicker.kt
new file mode 100644
index 0000000000..79832a2ad1
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/ImagePicker.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.graphics.BitmapFactory
+import android.graphics.ImageDecoder
+import android.net.Uri
+import android.os.Build
+import android.provider.MediaStore
+import androidx.exifinterface.media.ExifInterface
+import androidx.fragment.app.Fragment
+import im.vector.riotx.multipicker.entity.MultiPickerImageType
+
+class ImagePicker(override val requestCode: Int) : Picker(requestCode) {
+
+ override fun startWith(activity: Activity) {
+ activity.startActivityForResult(createIntent(), requestCode)
+ }
+
+ override fun startWith(fragment: Fragment) {
+ fragment.startActivityForResult(createIntent(), requestCode)
+ }
+
+ override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List {
+ if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) {
+ return emptyList()
+ }
+
+ val imageList = mutableListOf()
+
+ val selectedUriList = mutableListOf()
+ val dataUri = data?.data
+ val clipData = data?.clipData
+
+ if (clipData != null) {
+ for (i in 0 until clipData.itemCount) {
+ selectedUriList.add(clipData.getItemAt(i).uri)
+ }
+ } else if (dataUri != null) {
+ selectedUriList.add(dataUri)
+ }
+
+ selectedUriList.forEach { selectedUri ->
+ val projection = arrayOf(
+ MediaStore.Images.Media.DISPLAY_NAME,
+ MediaStore.Images.Media.SIZE
+ )
+
+ context.contentResolver.query(
+ selectedUri,
+ projection,
+ null,
+ null,
+ null
+ )?.use { cursor ->
+ val nameColumn = cursor.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME)
+ val sizeColumn = cursor.getColumnIndex(MediaStore.Images.Media.SIZE)
+
+ if (cursor.moveToNext()) {
+ val name = cursor.getString(nameColumn)
+ val size = cursor.getLong(sizeColumn)
+
+ var orientation = 0
+
+ val bitmap = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
+ ImageDecoder.decodeBitmap(ImageDecoder.createSource(context.contentResolver, selectedUri))
+ } else {
+ context.contentResolver.openInputStream(selectedUri)?.use { inputStream ->
+ BitmapFactory.decodeStream(inputStream)
+ }
+ }
+
+ context.contentResolver.openInputStream(selectedUri)?.use { inputStream ->
+ try {
+ ExifInterface(inputStream).let {
+ orientation = it.rotationDegrees
+ }
+ } catch (e: Exception) {
+ e.printStackTrace()
+ }
+ }
+
+ imageList.add(
+ MultiPickerImageType(
+ name,
+ size,
+ context.contentResolver.getType(selectedUri),
+ selectedUri,
+ bitmap?.width ?: 0,
+ bitmap?.height ?: 0,
+ orientation
+ )
+ )
+ }
+ }
+ }
+ return imageList
+ }
+
+ private fun createIntent(): Intent {
+ return Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ putExtra(Intent.EXTRA_ALLOW_MULTIPLE, !single)
+ type = "image/*"
+ }
+ }
+}
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/MultiPicker.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/MultiPicker.kt
new file mode 100644
index 0000000000..d10346c903
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/MultiPicker.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker
+
+class MultiPicker {
+
+ companion object Type {
+ val IMAGE by lazy { MultiPicker() }
+ val FILE by lazy { MultiPicker() }
+ val VIDEO by lazy { MultiPicker() }
+ val AUDIO by lazy { MultiPicker() }
+ val CONTACT by lazy { MultiPicker() }
+
+ const val REQUEST_CODE_PICK_IMAGE = 5000
+ const val REQUEST_CODE_PICK_VIDEO = 5001
+ const val REQUEST_CODE_PICK_FILE = 5002
+ const val REQUEST_CODE_PICK_AUDIO = 5003
+ const val REQUEST_CODE_PICK_CONTACT = 5004
+
+ @Suppress("UNCHECKED_CAST")
+ fun get(type: MultiPicker): T {
+ return when (type) {
+ IMAGE -> ImagePicker(REQUEST_CODE_PICK_IMAGE) as T
+ VIDEO -> VideoPicker(REQUEST_CODE_PICK_VIDEO) as T
+ FILE -> FilePicker(REQUEST_CODE_PICK_FILE) as T
+ AUDIO -> AudioPicker(REQUEST_CODE_PICK_AUDIO) as T
+ CONTACT -> ContactPicker(REQUEST_CODE_PICK_CONTACT) as T
+ else -> throw IllegalArgumentException("Unsupported type $type")
+ }
+ }
+ }
+}
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/Picker.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/Picker.kt
new file mode 100644
index 0000000000..62dff35062
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/Picker.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import androidx.fragment.app.Fragment
+
+abstract class Picker(open val requestCode: Int) {
+
+ protected var single = false
+
+ abstract fun startWith(activity: Activity)
+
+ abstract fun startWith(fragment: Fragment)
+
+ abstract fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List
+
+ fun single(): Picker {
+ single = true
+ return this
+ }
+}
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/VideoPicker.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/VideoPicker.kt
new file mode 100644
index 0000000000..8066c0b43e
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/VideoPicker.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker
+
+import android.app.Activity
+import android.content.Context
+import android.content.Intent
+import android.media.MediaMetadataRetriever
+import android.net.Uri
+import android.provider.MediaStore
+import androidx.fragment.app.Fragment
+import im.vector.riotx.multipicker.entity.MultiPickerVideoType
+
+class VideoPicker(override val requestCode: Int) : Picker(requestCode) {
+
+ override fun startWith(activity: Activity) {
+ activity.startActivityForResult(createIntent(), requestCode)
+ }
+
+ override fun startWith(fragment: Fragment) {
+ fragment.startActivityForResult(createIntent(), requestCode)
+ }
+
+ override fun getSelectedFiles(context: Context, requestCode: Int, resultCode: Int, data: Intent?): List {
+ if (requestCode != this.requestCode && resultCode != Activity.RESULT_OK) {
+ return emptyList()
+ }
+
+ val videoList = mutableListOf()
+
+ val selectedUriList = mutableListOf()
+ val dataUri = data?.data
+ val clipData = data?.clipData
+
+ if (clipData != null) {
+ for (i in 0 until clipData.itemCount) {
+ selectedUriList.add(clipData.getItemAt(i).uri)
+ }
+ } else if (dataUri != null) {
+ selectedUriList.add(dataUri)
+ }
+
+ selectedUriList.forEach { selectedUri ->
+ val projection = arrayOf(
+ MediaStore.Video.Media.DISPLAY_NAME,
+ MediaStore.Video.Media.SIZE
+ )
+
+ context.contentResolver.query(
+ selectedUri,
+ projection,
+ null,
+ null,
+ null
+ )?.use { cursor ->
+ val nameColumn = cursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME)
+ val sizeColumn = cursor.getColumnIndex(MediaStore.Video.Media.SIZE)
+
+ if (cursor.moveToNext()) {
+ val name = cursor.getString(nameColumn)
+ val size = cursor.getLong(sizeColumn)
+ var duration = 0L
+ var width = 0
+ var height = 0
+ var orientation = 0
+
+ context.contentResolver.openFileDescriptor(selectedUri, "r")?.use { pfd ->
+ val mediaMetadataRetriever = MediaMetadataRetriever()
+ mediaMetadataRetriever.setDataSource(pfd.fileDescriptor)
+ duration = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).toLong()
+ width = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH).toInt()
+ height = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT).toInt()
+ orientation = mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION).toInt()
+ }
+
+ videoList.add(
+ MultiPickerVideoType(
+ name,
+ size,
+ context.contentResolver.getType(selectedUri),
+ selectedUri,
+ width,
+ height,
+ orientation,
+ duration
+ )
+ )
+ }
+ }
+ }
+ return videoList
+ }
+
+ private fun createIntent(): Intent {
+ return Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
+ addCategory(Intent.CATEGORY_OPENABLE)
+ putExtra(Intent.EXTRA_ALLOW_MULTIPLE, !single)
+ type = "video/*"
+ }
+ }
+}
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerAudioType.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerAudioType.kt
new file mode 100644
index 0000000000..6afe022024
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerAudioType.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker.entity
+
+import android.net.Uri
+
+data class MultiPickerAudioType(
+ override val displayName: String?,
+ override val size: Long,
+ override val mimeType: String?,
+ override val contentUri: Uri,
+ val duration: Long
+) : MultiPickerBaseType
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerBaseType.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerBaseType.kt
new file mode 100644
index 0000000000..777e4d8441
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerBaseType.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker.entity
+
+import android.net.Uri
+
+interface MultiPickerBaseType {
+ val displayName: String?
+ val size: Long
+ val mimeType: String?
+ val contentUri: Uri
+}
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerContactType.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerContactType.kt
new file mode 100644
index 0000000000..565304a546
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerContactType.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker.entity
+
+data class MultiPickerContactType(
+ val displayName: String,
+ val photoUri: String?,
+ val phoneNumberList: List,
+ val emailList: List
+) {
+ private val FORMAT_CONTACT = "Name: %s, Photo: %s, Phones: %s, Emails: %s"
+
+ override fun toString(): String {
+ val phoneNumberString = phoneNumberList.joinToString(separator = ", ", prefix = "[", postfix = "]")
+ val emailString = emailList.joinToString(separator = ", ", prefix = "[", postfix = "]")
+ return String.format(FORMAT_CONTACT, displayName, photoUri, phoneNumberString, emailString)
+ }
+}
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerFileType.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerFileType.kt
new file mode 100644
index 0000000000..5417520d28
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerFileType.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker.entity
+
+import android.net.Uri
+
+data class MultiPickerFileType(
+ override val displayName: String?,
+ override val size: Long,
+ override val mimeType: String?,
+ override val contentUri: Uri
+) : MultiPickerBaseType
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerImageType.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerImageType.kt
new file mode 100644
index 0000000000..b1aef171b4
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerImageType.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker.entity
+
+import android.net.Uri
+
+data class MultiPickerImageType(
+ override val displayName: String?,
+ override val size: Long,
+ override val mimeType: String?,
+ override val contentUri: Uri,
+ val width: Int,
+ val height: Int,
+ val orientation: Int
+) : MultiPickerBaseType
diff --git a/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerVideoType.kt b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerVideoType.kt
new file mode 100644
index 0000000000..ba9a8d233e
--- /dev/null
+++ b/multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerVideoType.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker.entity
+
+import android.net.Uri
+
+data class MultiPickerVideoType(
+ override val displayName: String?,
+ override val size: Long,
+ override val mimeType: String?,
+ override val contentUri: Uri,
+ val width: Int,
+ val height: Int,
+ val orientation: Int,
+ val duration: Long
+) : MultiPickerBaseType
diff --git a/multipicker/src/test/java/im/vector/riotx/multipicker/ExampleUnitTest.kt b/multipicker/src/test/java/im/vector/riotx/multipicker/ExampleUnitTest.kt
new file mode 100644
index 0000000000..07e464699f
--- /dev/null
+++ b/multipicker/src/test/java/im/vector/riotx/multipicker/ExampleUnitTest.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 New Vector Ltd
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package im.vector.riotx.multipicker
+
+import junit.framework.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)
+ }
+}
diff --git a/settings.gradle b/settings.gradle
index d020abade4..04307e89d9 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1,2 @@
include ':vector', ':matrix-sdk-android', ':matrix-sdk-android-rx', ':diff-match-patch'
+include ':multipicker'
diff --git a/vector/build.gradle b/vector/build.gradle
index 66ec6808c8..447ad7a767 100644
--- a/vector/build.gradle
+++ b/vector/build.gradle
@@ -254,6 +254,7 @@ dependencies {
implementation project(":matrix-sdk-android")
implementation project(":matrix-sdk-android-rx")
implementation project(":diff-match-patch")
+ implementation project(":multipicker")
implementation 'com.android.support:multidex:1.0.3'
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml
index 2e56e20ce7..092817a6cc 100644
--- a/vector/src/main/AndroidManifest.xml
+++ b/vector/src/main/AndroidManifest.xml
@@ -8,6 +8,7 @@
+
diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
index e748478e6a..30f9551612 100644
--- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt
@@ -156,6 +156,7 @@ import im.vector.riotx.features.reactions.EmojiReactionPickerActivity
import im.vector.riotx.features.settings.VectorPreferences
import im.vector.riotx.features.share.SharedData
import im.vector.riotx.features.themes.ThemeUtils
+import im.vector.riotx.multipicker.MultiPicker
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.android.parcel.Parcelize
@@ -290,9 +291,9 @@ class RoomDetailFragment @Inject constructor(
roomDetailViewModel.observeViewEvents {
when (it) {
- is RoomDetailViewEvents.Failure -> showErrorInSnackbar(it.throwable)
- is RoomDetailViewEvents.OnNewTimelineEvents -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
- is RoomDetailViewEvents.ActionSuccess -> displayRoomDetailActionSuccess(it)
+ is RoomDetailViewEvents.Failure -> showErrorInSnackbar(it.throwable)
+ is RoomDetailViewEvents.OnNewTimelineEvents -> scrollOnNewMessageCallback.addNewTimelineEventIds(it.eventIds)
+ is RoomDetailViewEvents.ActionSuccess -> displayRoomDetailActionSuccess(it)
is RoomDetailViewEvents.ActionFailure -> displayRoomDetailActionFailure(it)
is RoomDetailViewEvents.ShowMessage -> showSnackWithMessage(it.message, Snackbar.LENGTH_LONG)
is RoomDetailViewEvents.NavigateToEvent -> navigateToEvent(it)
@@ -516,6 +517,24 @@ class RoomDetailFragment @Inject constructor(
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+ when (requestCode) {
+ MultiPicker.REQUEST_CODE_PICK_IMAGE -> {
+ MultiPicker.get(MultiPicker.IMAGE).getSelectedFiles(requireContext(), requestCode, resultCode, data)
+ }
+ MultiPicker.REQUEST_CODE_PICK_VIDEO -> {
+ MultiPicker.get(MultiPicker.VIDEO).getSelectedFiles(requireContext(), requestCode, resultCode, data)
+ }
+ MultiPicker.REQUEST_CODE_PICK_FILE -> {
+ MultiPicker.get(MultiPicker.FILE).getSelectedFiles(requireContext(), requestCode, resultCode, data)
+ }
+ MultiPicker.REQUEST_CODE_PICK_AUDIO -> {
+ MultiPicker.get(MultiPicker.AUDIO).getSelectedFiles(requireContext(), requestCode, resultCode, data)
+ }
+ MultiPicker.REQUEST_CODE_PICK_CONTACT -> {
+ MultiPicker.get(MultiPicker.CONTACT).getSelectedFiles(requireContext(), requestCode, resultCode, data)
+ }
+ }
+
val hasBeenHandled = attachmentsHelper.onActivityResult(requestCode, resultCode, data)
if (!hasBeenHandled && resultCode == RESULT_OK && data != null) {
when (requestCode) {
@@ -1351,10 +1370,10 @@ class RoomDetailFragment @Inject constructor(
private fun launchAttachmentProcess(type: AttachmentTypeSelectorView.Type) {
when (type) {
AttachmentTypeSelectorView.Type.CAMERA -> attachmentsHelper.openCamera()
- AttachmentTypeSelectorView.Type.FILE -> attachmentsHelper.selectFile()
- AttachmentTypeSelectorView.Type.GALLERY -> attachmentsHelper.selectGallery()
- AttachmentTypeSelectorView.Type.AUDIO -> attachmentsHelper.selectAudio()
- AttachmentTypeSelectorView.Type.CONTACT -> attachmentsHelper.selectContact()
+ AttachmentTypeSelectorView.Type.FILE -> MultiPicker.get(MultiPicker.FILE).startWith(this)
+ AttachmentTypeSelectorView.Type.GALLERY -> MultiPicker.get(MultiPicker.IMAGE).startWith(this)
+ AttachmentTypeSelectorView.Type.AUDIO -> MultiPicker.get(MultiPicker.AUDIO).startWith(this)
+ AttachmentTypeSelectorView.Type.CONTACT -> MultiPicker.get(MultiPicker.CONTACT).startWith(this)
AttachmentTypeSelectorView.Type.STICKER -> vectorBaseActivity.notImplemented("Adding stickers")
}.exhaustive
}