diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt index de7c5285b..7afefc010 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeActivity.kt @@ -31,7 +31,6 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.os.SystemClock -import android.provider.ContactsContract import android.provider.MediaStore import android.speech.RecognizerIntent import android.speech.SpeechRecognizer @@ -45,6 +44,8 @@ import android.view.MenuItem import android.view.View import android.widget.SeekBar import android.widget.Toast +import androidx.activity.result.PickVisualMediaRequest +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AlertDialog import androidx.constraintlayout.widget.ConstraintSet import androidx.core.app.ActivityCompat @@ -164,6 +165,40 @@ class ComposeActivity : QkThemedActivity(), ComposeView { private var cameraDestination: Uri? = null + private val pickMedia = registerForActivityResult( + ActivityResultContracts.PickMultipleVisualMedia() + ) { uris -> + uris.forEach(attachAnyFileSelectedIntent::onNext) + } + + private val pickFilesWithDocsUI = registerForActivityResult( + ActivityResultContracts.GetMultipleContents() + ) { uris -> + uris.forEach(attachAnyFileSelectedIntent::onNext) + } + + private val pickFilesWithChooser = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + if (result.resultCode == RESULT_OK) { + val data = result.data ?: return@registerForActivityResult + + val uris = data.clipData?.let { clipData -> + (0 until clipData.itemCount).mapNotNull { clipData.getItemAt(it).uri } + } ?: listOfNotNull(data.data) + + uris.forEach { uri -> + attachAnyFileSelectedIntent.onNext(uri) + } + } + } + + private val pickContact = registerForActivityResult( + ActivityResultContracts.PickContact() + ) { uri -> + uri?.let { contactSelectedIntent.onNext(it) } + } + private fun getSeekBarUpdater(): ObservableSubscribeProxy { return Observable.interval(500, TimeUnit.MILLISECONDS) .subscribeOn(Schedulers.single()) @@ -610,10 +645,7 @@ class ComposeActivity : QkThemedActivity(), ComposeView { } override fun requestContact() { - val intent = Intent(Intent.ACTION_PICK) - .setType(ContactsContract.Contacts.CONTENT_TYPE) - - startActivityForResult(Intent.createChooser(intent, null), ComposeView.ATTACH_CONTACT_REQUEST_CODE) + pickContact.launch(null) } override fun showContacts(sharing: Boolean, chips: List) { @@ -658,14 +690,29 @@ class ComposeActivity : QkThemedActivity(), ComposeView { startActivityForResult(Intent.createChooser(intent, null), ComposeView.TAKE_PHOTOS_REQUEST_CODE) } - override fun requestGallery(mimeType: String, requestCode: Int) { - val intent = Intent(Intent.ACTION_PICK) - .putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) - .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) - .putExtra(Intent.EXTRA_LOCAL_ONLY, false) - .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - .setType(mimeType) - startActivityForResult(Intent.createChooser(intent, null), requestCode) + override fun requestGallery() { + // TODO: Use https://developer.android.com/reference/androidx/activity/result/contract/ActivityResultContracts.PickVisualMedia.DefaultTab.AlbumsTab + // here to make it clearer that videos can be selected as well. + pickMedia.launch( + PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo) + ) + } + + override fun requestFilePicker() { + // On older Android versions, let's still use the older method of creating a chooser, + // and allowing the user to pick which file they would like. + // On Android 17 however, we must use Documents UI, so let's do it properly. + if (Build.VERSION.SDK_INT >= 37) { + pickFilesWithDocsUI.launch("*/*") + } else { + val intent = Intent(Intent.ACTION_PICK) + .putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) + .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) + .putExtra(Intent.EXTRA_LOCAL_ONLY, false) + .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + .setType("*/*") + pickFilesWithChooser.launch(Intent.createChooser(intent, null)) + } } override fun setDraft(draft: String) { @@ -760,18 +807,6 @@ class ComposeActivity : QkThemedActivity(), ComposeView { cameraDestination?.let(attachAnyFileSelectedIntent::onNext) } - ComposeView.ATTACH_FILE_REQUEST_CODE -> { - data?.clipData?.itemCount - ?.let { count -> 0 until count } - ?.mapNotNull { i -> data.clipData?.getItemAt(i)?.uri } - ?.forEach(attachAnyFileSelectedIntent::onNext) - ?: data?.data?.let(attachAnyFileSelectedIntent::onNext) - } - - ComposeView.ATTACH_CONTACT_REQUEST_CODE -> { - data?.data?.let(contactSelectedIntent::onNext) - } - ComposeView.SPEECH_RECOGNITION_REQUEST_CODE -> { // check returned results are good val match = data?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS) diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt index 3f6618784..b2b049ca6 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeView.kt @@ -36,8 +36,6 @@ interface ComposeView : QkView { companion object { const val SELECT_CONTACT_REQUEST_CODE = 0 const val TAKE_PHOTOS_REQUEST_CODE = 1 - const val ATTACH_CONTACT_REQUEST_CODE = 3 - const val ATTACH_FILE_REQUEST_CODE = 4 const val SPEECH_RECOGNITION_REQUEST_CODE = 5 const val CAMERA_DESTINATION_KEY = "camera_destination" @@ -104,7 +102,8 @@ interface ComposeView : QkView { fun themeChanged() fun showKeyboard() fun requestCamera() - fun requestGallery(mimeType: String, requestCode: Int) + fun requestGallery() + fun requestFilePicker() fun requestDatePicker() fun requestContact() fun setDraft(draft: String) diff --git a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt index 51912e962..e9895cea3 100644 --- a/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt +++ b/presentation/src/main/java/com/moez/QKSMS/feature/compose/ComposeViewModel.kt @@ -830,17 +830,17 @@ class ComposeViewModel @Inject constructor( view.requestCamera() } - // pick a photo (specifically) from image provider apps + // pick a photo or video from image provider apps view.attachImageFileIntent .doOnNext { newState { copy(attaching = false) } } .autoDisposable(view.scope()) - .subscribe { view.requestGallery("image/*", ComposeView.ATTACH_FILE_REQUEST_CODE) } + .subscribe { view.requestGallery() } // pick any file from any provider apps view.attachAnyFileIntent .doOnNext { newState { copy(attaching = false) } } .autoDisposable(view.scope()) - .subscribe { view.requestGallery("*/*", ComposeView.ATTACH_FILE_REQUEST_CODE) } + .subscribe { view.requestFilePicker() } // Choose a time to schedule the message view.scheduleIntent