From 6db0de321c704ebc43220b4a6905ba2944239f3a Mon Sep 17 00:00:00 2001 From: onurays Date: Fri, 20 Mar 2020 12:12:59 +0300 Subject: [PATCH] Initial implementation of multipicker. --- .gitignore | 134 +++++++++++++++++ matrix-sdk-android/build.gradle | 2 +- multipicker/.gitignore | 1 + multipicker/build.gradle | 54 +++++++ multipicker/consumer-rules.pro | 0 multipicker/proguard-rules.pro | 21 +++ .../multipicker/ExampleInstrumentedTest.kt | 39 +++++ multipicker/src/main/AndroidManifest.xml | 2 + .../vector/riotx/multipicker/AudioPicker.kt | 106 ++++++++++++++ .../vector/riotx/multipicker/ContactPicker.kt | 135 ++++++++++++++++++ .../im/vector/riotx/multipicker/FilePicker.kt | 86 +++++++++++ .../vector/riotx/multipicker/ImagePicker.kt | 124 ++++++++++++++++ .../vector/riotx/multipicker/MultiPicker.kt | 46 ++++++ .../im/vector/riotx/multipicker/Picker.kt | 38 +++++ .../vector/riotx/multipicker/VideoPicker.kt | 115 +++++++++++++++ .../entity/MultiPickerAudioType.kt | 27 ++++ .../multipicker/entity/MultiPickerBaseType.kt | 26 ++++ .../entity/MultiPickerContactType.kt | 32 +++++ .../multipicker/entity/MultiPickerFileType.kt | 26 ++++ .../entity/MultiPickerImageType.kt | 29 ++++ .../entity/MultiPickerVideoType.kt | 30 ++++ .../riotx/multipicker/ExampleUnitTest.kt | 32 +++++ settings.gradle | 1 + vector/build.gradle | 1 + vector/src/main/AndroidManifest.xml | 1 + .../home/room/detail/RoomDetailFragment.kt | 33 ++++- 26 files changed, 1133 insertions(+), 8 deletions(-) create mode 100644 multipicker/.gitignore create mode 100644 multipicker/build.gradle create mode 100644 multipicker/consumer-rules.pro create mode 100644 multipicker/proguard-rules.pro create mode 100644 multipicker/src/androidTest/java/im/vector/riotx/multipicker/ExampleInstrumentedTest.kt create mode 100644 multipicker/src/main/AndroidManifest.xml create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/AudioPicker.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/ContactPicker.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/FilePicker.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/ImagePicker.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/MultiPicker.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/Picker.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/VideoPicker.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerAudioType.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerBaseType.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerContactType.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerFileType.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerImageType.kt create mode 100644 multipicker/src/main/java/im/vector/riotx/multipicker/entity/MultiPickerVideoType.kt create mode 100644 multipicker/src/test/java/im/vector/riotx/multipicker/ExampleUnitTest.kt 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 }