Using new unified API for content insertion

This commit is contained in:
Sylvain Berfini 2021-09-13 17:20:44 +02:00
parent f7ba6fc87c
commit 57af8806f7
5 changed files with 101 additions and 38 deletions

View file

@ -22,6 +22,7 @@ Group changes to describe their impact on the project, as follows:
### Changed ### Changed
- UI has been reworked around SlidingPane component to better handle tablets & foldable devices - UI has been reworked around SlidingPane component to better handle tablets & foldable devices
- Animations have been replaced to use com.google.android.material.transition ones - Animations have been replaced to use com.google.android.material.transition ones
- Using new [Unified Content API](https://developer.android.com/about/versions/12/features/unified-content-api) to share files from keyboard (or other sources)
- Bumped dependencies, gradle updated from 4.2.2 to 7.0.2 - Bumped dependencies, gradle updated from 4.2.2 to 7.0.2
- Target Android SDK version set to 31 (Android 12) - Target Android SDK version set to 31 (Android 12)
- SDK updated to 5.1.0 release - SDK updated to 5.1.0 release

View file

@ -38,7 +38,9 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import java.io.File import java.io.File
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.linphone.LinphoneApplication.Companion.coreContext import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.LinphoneApplication.Companion.corePreferences import org.linphone.LinphoneApplication.Companion.corePreferences
import org.linphone.R import org.linphone.R
@ -449,6 +451,24 @@ class DetailChatRoomFragment : MasterFragment<ChatRoomDetailFragmentBinding, Cha
} }
} }
sharedViewModel.richContentUri.observe(
viewLifecycleOwner,
{
it.consume { uri ->
Log.i("[Chat] Found rich content URI: $uri")
lifecycleScope.launch {
withContext(Dispatchers.Main) {
val path = FileUtils.getFilePath(requireContext(), uri)
Log.i("[Chat] Rich content URI: $uri matching path is: $path")
if (path != null) {
chatSendingViewModel.addAttachment(path)
}
}
}
}
}
)
sharedViewModel.messageToForwardEvent.observe( sharedViewModel.messageToForwardEvent.observe(
viewLifecycleOwner, viewLifecycleOwner,
{ {

View file

@ -0,0 +1,50 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.activities.main.chat.receivers
import android.content.ClipData
import android.net.Uri
import android.view.View
import androidx.core.util.component1
import androidx.core.util.component2
import androidx.core.view.ContentInfoCompat
import androidx.core.view.OnReceiveContentListener
import org.linphone.core.tools.Log
class RichContentReceiver(private val contentReceived: (uri: Uri) -> Unit) : OnReceiveContentListener {
companion object {
val MIME_TYPES = arrayOf("image/png", "image/gif", "image/jpeg")
}
override fun onReceiveContent(view: View, payload: ContentInfoCompat): ContentInfoCompat? {
val (uriContent, remaining) = payload.partition { item -> item.uri != null }
if (uriContent != null) {
val clip: ClipData = uriContent.clip
for (i in 0 until clip.itemCount) {
val uri: Uri = clip.getItemAt(i).uri
Log.i("[Content Receiver] Found URI: $uri")
contentReceived(uri)
}
}
// Return anything that your app didn't handle. This preserves the default platform
// behavior for text and anything else that you aren't implementing custom handling for.
return remaining
}
}

View file

@ -19,61 +19,49 @@
*/ */
package org.linphone.activities.main.chat.views package org.linphone.activities.main.chat.views
import android.app.Activity
import android.content.Context import android.content.Context
import android.os.Bundle
import android.util.AttributeSet import android.util.AttributeSet
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputConnection
import androidx.appcompat.widget.AppCompatEditText import androidx.appcompat.widget.AppCompatEditText
import androidx.core.view.inputmethod.EditorInfoCompat import androidx.core.view.ViewCompat
import androidx.core.view.inputmethod.InputConnectionCompat import androidx.lifecycle.ViewModelProvider
import androidx.core.view.inputmethod.InputContentInfoCompat import androidx.lifecycle.ViewModelStoreOwner
import org.linphone.activities.main.chat.receivers.RichContentReceiver
import org.linphone.activities.main.viewmodels.SharedMainViewModel
import org.linphone.core.tools.Log
import org.linphone.utils.Event
/** /**
* Allows for image input inside an EditText, usefull for keyboards with gif support for example. * Allows for image input inside an EditText, usefull for keyboards with gif support for example.
*/ */
class RichEditText : AppCompatEditText { class RichEditText : AppCompatEditText {
private var mListener: RichInputListener? = null constructor(context: Context) : super(context) {
private var mSupportedMimeTypes: Array<String>? = null initReceiveContentListener()
interface RichInputListener {
fun onCommitContent(
inputContentInfo: InputContentInfoCompat,
flags: Int,
opts: Bundle?,
contentMimeTypes: Array<String>?
): Boolean
} }
constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
initReceiveContentListener()
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super( constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
context, context,
attrs, attrs,
defStyleAttr defStyleAttr
) ) {
initReceiveContentListener()
fun setListener(listener: RichInputListener) {
mListener = listener
mSupportedMimeTypes = arrayOf("image/png", "image/gif", "image/jpeg")
} }
override fun onCreateInputConnection(editorInfo: EditorInfo): InputConnection? { private fun initReceiveContentListener() {
val ic = super.onCreateInputConnection(editorInfo) ViewCompat.setOnReceiveContentListener(
EditorInfoCompat.setContentMimeTypes(editorInfo, mSupportedMimeTypes) this, RichContentReceiver.MIME_TYPES,
RichContentReceiver { uri ->
val callback = Log.i("[Rich Edit Text] Received URI: $uri")
InputConnectionCompat.OnCommitContentListener { inputContentInfo, flags, opts -> val activity = context as Activity
val listener = mListener val sharedViewModel = activity.run {
listener?.onCommitContent( ViewModelProvider(activity as ViewModelStoreOwner).get(SharedMainViewModel::class.java)
inputContentInfo, flags, opts, mSupportedMimeTypes }
) ?: false sharedViewModel.richContentUri.value = Event(uri)
} }
)
return if (ic != null) {
InputConnectionCompat.createWrapper(ic, editorInfo, callback)
} else null
} }
} }

View file

@ -19,6 +19,7 @@
*/ */
package org.linphone.activities.main.viewmodels package org.linphone.activities.main.viewmodels
import android.net.Uri
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import org.linphone.activities.main.history.data.GroupedCallLogData import org.linphone.activities.main.history.data.GroupedCallLogData
@ -66,6 +67,9 @@ class SharedMainViewModel : ViewModel() {
var chatRoomSubject: String = "" var chatRoomSubject: String = ""
// When using keyboard to share gif or other, see RichContentReceiver & RichEditText classes
val richContentUri = MutableLiveData<Event<Uri>>()
/* Contacts */ /* Contacts */
val contactFragmentOpenedEvent: MutableLiveData<Event<Boolean>> by lazy { val contactFragmentOpenedEvent: MutableLiveData<Event<Boolean>> by lazy {