From c7a4d3419211b47f109b158531eda8897fa02380 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 22 Oct 2019 12:37:59 +0200 Subject: [PATCH] Attachments: handle rich content from keyboard --- .../features/attachments/AttachmentsHelper.kt | 21 ++++++++------- .../home/room/detail/RoomDetailFragment.kt | 13 ++++++++++ ...omposerEditText.kt => ComposerEditText.kt} | 26 +++++++++++-------- .../room/detail/composer/TextComposerView.kt | 15 +++++++++-- .../features/share/IncomingShareActivity.kt | 5 +++- ...constraint_set_composer_layout_compact.xml | 2 +- ...onstraint_set_composer_layout_expanded.xml | 2 +- .../main/res/layout/merge_composer_layout.xml | 2 +- 8 files changed, 59 insertions(+), 27 deletions(-) rename vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/{TextComposerEditText.kt => ComposerEditText.kt} (76%) diff --git a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt index 1e2d59a4c3..8a4a0d9309 100644 --- a/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt +++ b/vector/src/main/java/im/vector/riotx/features/attachments/AttachmentsHelper.kt @@ -16,12 +16,12 @@ package im.vector.riotx.features.attachments import android.app.Activity +import android.content.Context import android.content.Intent import android.os.Bundle import androidx.fragment.app.Fragment import com.kbeanie.multipicker.api.Picker.* import com.kbeanie.multipicker.core.PickerManager -import com.kbeanie.multipicker.utils.IntentUtils import im.vector.matrix.android.BuildConfig import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.riotx.core.platform.Restorable @@ -34,15 +34,16 @@ private const val PENDING_TYPE_KEY = "PENDING_TYPE_KEY" * This class helps to handle attachments by providing simple methods. * The process is asynchronous and you must implement [Callback] methods to get the data or a failure. */ -class AttachmentsHelper private constructor(private val pickerManagerFactory: PickerManagerFactory) : Restorable { +class AttachmentsHelper private constructor(private val context: Context, + private val pickerManagerFactory: PickerManagerFactory) : Restorable { companion object { fun create(fragment: Fragment, callback: Callback): AttachmentsHelper { - return AttachmentsHelper(FragmentPickerManagerFactory(fragment, callback)) + return AttachmentsHelper(fragment.requireContext(), FragmentPickerManagerFactory(fragment, callback)) } fun create(activity: Activity, callback: Callback): AttachmentsHelper { - return AttachmentsHelper(ActivityPickerManagerFactory(activity, callback)) + return AttachmentsHelper(activity, ActivityPickerManagerFactory(activity, callback)) } } @@ -163,16 +164,16 @@ class AttachmentsHelper private constructor(private val pickerManagerFactory: Pi * * @return true if it can handle the intent data, false otherwise */ - fun handleShare(intent: Intent): Boolean { - val type = intent.type ?: return false + fun handleShareIntent(intent: Intent): Boolean { + val type = intent.resolveType(context) ?: return false if (type.startsWith("image")) { - imagePicker.submit(IntentUtils.getPickerIntentForSharing(intent)) + imagePicker.submit(intent) } else if (type.startsWith("video")) { - videoPicker.submit(IntentUtils.getPickerIntentForSharing(intent)) + videoPicker.submit(intent) } else if (type.startsWith("audio")) { - videoPicker.submit(IntentUtils.getPickerIntentForSharing(intent)) + videoPicker.submit(intent) } else if (type.startsWith("application") || type.startsWith("file") || type.startsWith("*")) { - filePicker.submit(IntentUtils.getPickerIntentForSharing(intent)) + filePicker.submit(intent) } else { return false } 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 6fa4fdceaf..567650c606 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 @@ -605,6 +605,19 @@ class RoomDetailFragment : composerLayout.composerRelatedMessageCloseButton.setOnClickListener { roomDetailViewModel.process(RoomDetailActions.ExitSpecialMode(composerLayout.composerEditText.text.toString())) } + composerLayout.callback = object : TextComposerView.Callback { + override fun onRichContentSelected(contentUri: Uri): Boolean { + val shareIntent = Intent().apply { + action = Intent.ACTION_SEND + data = contentUri + } + val isHandled = attachmentsHelper.handleShareIntent(shareIntent) + if (!isHandled) { + Toast.makeText(requireContext(), R.string.error_handling_incoming_share, Toast.LENGTH_SHORT).show() + } + return isHandled + } + } } private fun setupAttachmentButton() { diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerEditText.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/ComposerEditText.kt similarity index 76% rename from vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerEditText.kt rename to vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/ComposerEditText.kt index 04d63f39b8..3b1165b016 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerEditText.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/ComposerEditText.kt @@ -18,38 +18,42 @@ package im.vector.riotx.features.home.room.detail.composer import android.content.Context +import android.net.Uri import android.os.Build import android.util.AttributeSet import android.view.inputmethod.EditorInfo import android.view.inputmethod.InputConnection -import androidx.appcompat.widget.AppCompatEditText +import android.widget.EditText import androidx.core.view.inputmethod.EditorInfoCompat import androidx.core.view.inputmethod.InputConnectionCompat -class TextComposerEditText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) - : AppCompatEditText(context, attrs, defStyleAttr) { +class ComposerEditText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.editTextStyle) + : EditText(context, attrs, defStyleAttr) { + + interface Callback { + fun onRichContentSelected(contentUri: Uri): Boolean + } + + var callback: Callback? = null override fun onCreateInputConnection(editorInfo: EditorInfo): InputConnection { val ic: InputConnection = super.onCreateInputConnection(editorInfo) - EditorInfoCompat.setContentMimeTypes(editorInfo, arrayOf("image/png")) + EditorInfoCompat.setContentMimeTypes(editorInfo, arrayOf("*/*")) val callback = - InputConnectionCompat.OnCommitContentListener { inputContentInfo, flags, opts -> + InputConnectionCompat.OnCommitContentListener { inputContentInfo, flags, _ -> val lacksPermission = (flags and InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0 - // read and display inputContentInfo asynchronously if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1 && lacksPermission) { try { inputContentInfo.requestPermission() } catch (e: Exception) { - return@OnCommitContentListener false // return false if failed + return@OnCommitContentListener false } } - // read and display inputContentInfo asynchronously. - // call inputContentInfo.releasePermission() as needed. - true // return true if succeeded + callback?.onRichContentSelected(inputContentInfo.contentUri) ?: false + } return InputConnectionCompat.createWrapper(ic, editorInfo, callback) } - } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt index 62df5d5e95..192a3a6977 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/composer/TextComposerView.kt @@ -17,9 +17,9 @@ package im.vector.riotx.features.home.room.detail.composer import android.content.Context +import android.net.Uri import android.util.AttributeSet import android.view.ViewGroup -import android.widget.EditText import android.widget.ImageButton import android.widget.ImageView import android.widget.TextView @@ -39,6 +39,12 @@ import im.vector.riotx.R class TextComposerView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : ConstraintLayout(context, attrs, defStyleAttr) { + interface Callback { + fun onRichContentSelected(contentUri: Uri): Boolean + } + + var callback: Callback? = null + @BindView(R.id.composer_related_message_sender) lateinit var composerRelatedMessageTitle: TextView @BindView(R.id.composer_related_message_preview) @@ -50,7 +56,7 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib @BindView(R.id.composer_related_message_close) lateinit var composerRelatedMessageCloseButton: ImageButton @BindView(R.id.composerEditText) - lateinit var composerEditText: EditText + lateinit var composerEditText: ComposerEditText @BindView(R.id.composer_avatar_view) lateinit var composerAvatarImageView: ImageView @@ -62,6 +68,11 @@ class TextComposerView @JvmOverloads constructor(context: Context, attrs: Attrib inflate(context, R.layout.merge_composer_layout, this) ButterKnife.bind(this) collapse(false) + composerEditText.callback = object : Callback, ComposerEditText.Callback { + override fun onRichContentSelected(contentUri: Uri): Boolean { + return callback?.onRichContentSelected(contentUri) ?: false + } + } } fun collapse(animate: Boolean = true, transitionComplete: (() -> Unit)? = null) { diff --git a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt index 197405aa53..0d2f9ee040 100644 --- a/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt +++ b/vector/src/main/java/im/vector/riotx/features/share/IncomingShareActivity.kt @@ -20,6 +20,7 @@ import android.content.ClipDescription import android.content.Intent import android.os.Bundle import android.widget.Toast +import com.kbeanie.multipicker.utils.IntentUtils import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.riotx.R import im.vector.riotx.core.di.ActiveSessionHolder @@ -64,7 +65,9 @@ class IncomingShareActivity : } attachmentsHelper = AttachmentsHelper.create(this, this).register() if (intent?.action == Intent.ACTION_SEND || intent?.action == Intent.ACTION_SEND_MULTIPLE) { - var isShareManaged = attachmentsHelper.handleShare(intent) + var isShareManaged = attachmentsHelper.handleShareIntent( + IntentUtils.getPickerIntentForSharing(intent) + ) if (!isShareManaged) { isShareManaged = handleTextShare(intent) } diff --git a/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml b/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml index b2fff0880b..ac04dfe3ec 100644 --- a/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml +++ b/vector/src/main/res/layout/constraint_set_composer_layout_compact.xml @@ -143,7 +143,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toEndOf="@id/attachmentButton" /> - - -