diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 816507750..cc1acb824 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -41,6 +41,7 @@
android:label="@string/app_name"
android:enableOnBackInvokedCallback="true"
android:theme="@style/Theme.Linphone"
+ android:localeConfig="@xml/locales_config"
tools:targetApi="34">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
+ handleMainIntent(intent, defaultDestination, isNewIntent)
+ }
+ Intent.ACTION_SEND -> {
+ handleSendIntent(intent, false)
+ }
+ Intent.ACTION_SEND_MULTIPLE -> {
+ handleSendIntent(intent, true)
+ }
+ }
+ }
+
+ private fun handleMainIntent(intent: Intent, defaultDestination: Int, isNewIntent: Boolean) {
+ val navGraph = findNavController().navInflater.inflate(R.navigation.main_nav_graph)
if (intent.hasExtra("Chat")) {
Log.i("$TAG New intent with [Chat] extra")
coreContext.postOnMainThread {
@@ -319,6 +342,69 @@ class MainActivity : AppCompatActivity() {
}
}
+ private fun handleSendIntent(intent: Intent, multiple: Boolean) {
+ val parcelablesUri = arrayListOf()
+ if (multiple) {
+ val parcelables = intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM)
+ for (parcelable in parcelables.orEmpty()) {
+ val uri = parcelable as? Uri
+ if (uri != null) {
+ Log.i("$TAG Found URI [$uri] in parcelable extra list")
+ parcelablesUri.add(uri)
+ }
+ }
+ } else {
+ val uri = intent.getParcelableExtra(Intent.EXTRA_STREAM) as? Uri
+ if (uri != null) {
+ Log.i("$TAG Found URI [$uri] in parcelable extra")
+ parcelablesUri.add(uri)
+ }
+ }
+
+ val list = arrayListOf()
+ lifecycleScope.launch() {
+ val deferred = arrayListOf>()
+ for (uri in parcelablesUri) {
+ deferred.add(async { FileUtils.getFilePath(this@MainActivity, uri, false) })
+ }
+
+ val shortcutId = intent.getStringExtra("android.intent.extra.shortcut.ID") // Intent.EXTRA_SHORTCUT_ID
+ if (shortcutId != null) {
+ Log.i("$TAG Found shortcut ID [$shortcutId]")
+ val pair = LinphoneUtils.getLocalAndPeerSipUrisFromChatRoomId(shortcutId)
+ if (pair != null) {
+ val localSipUri = pair.first
+ val remoteSipUri = pair.second
+ Log.i(
+ "$TAG Navigating to conversation with local [$localSipUri] and peer [$remoteSipUri] addresses, computed from shortcut ID"
+ )
+ intent.putExtra("LocalSipUri", localSipUri)
+ intent.putExtra("RemoteSipUri", remoteSipUri)
+ } else {
+ Log.e("$TAG Failed to parse shortcut ID, going to conversations list")
+ }
+ } else {
+ Log.i("$TAG Going into conversations list as no shortcut ID as found")
+ }
+
+ val navGraph = findNavController().navInflater.inflate(R.navigation.main_nav_graph)
+ navGraph.setStartDestination(R.id.conversationsFragment)
+
+ val paths = deferred.awaitAll()
+ for (path in paths) {
+ Log.i("$TAG Found file to share [$path] in intent")
+ if (path != null) list.add(path)
+ }
+ if (list.isNotEmpty()) {
+ sharedViewModel.filesToShareFromIntent.value = list
+ } else {
+ Log.w("$TAG Failed to find at least one file to share!")
+ }
+
+ findNavController().setGraph(navGraph, intent.extras)
+ }
+ }
+
private fun loadContacts() {
coreContext.contactsManager.loadContacts(this)
diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt
index 0a9bbcdd7..3194e4bba 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationFragment.kt
@@ -385,6 +385,17 @@ class ConversationFragment : GenericFragment() {
}
}
+ sharedViewModel.filesToShareFromIntent.observe(viewLifecycleOwner) { files ->
+ if (files.isNotEmpty()) {
+ Log.i("$TAG Found [${files.size}] files to share from intent")
+ for (path in files) {
+ viewModel.addAttachment(path)
+ }
+
+ sharedViewModel.filesToShareFromIntent.value = arrayListOf()
+ }
+ }
+
binding.sendArea.messageToSend.setControlEnterListener(object :
RichEditText.RichEditTextSendListener {
override fun onControlEnterPressedAndReleased() {
diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt
index daec0be86..aa246e42c 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationViewModel.kt
@@ -454,7 +454,9 @@ class ConversationViewModel @UiThread constructor() : ViewModel() {
list.add(model)
attachments.value = list
- isFileAttachmentsListOpen.value = true
+ if (list.isNotEmpty()) {
+ isFileAttachmentsListOpen.value = true
+ }
}
@UiThread
diff --git a/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt b/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt
index 9450a6409..4be2ff6b9 100644
--- a/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/viewmodel/SharedMainViewModel.kt
@@ -107,6 +107,8 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
MutableLiveData>()
}
+ val filesToShareFromIntent = MutableLiveData>()
+
var displayedChatRoom: ChatRoom? = null // Prevents the need to go look for the chat room
val showConversationEvent: MutableLiveData>> by lazy {
MutableLiveData>>()
diff --git a/app/src/main/java/org/linphone/utils/FileUtils.kt b/app/src/main/java/org/linphone/utils/FileUtils.kt
index 1c1f2059f..7bbcc6447 100644
--- a/app/src/main/java/org/linphone/utils/FileUtils.kt
+++ b/app/src/main/java/org/linphone/utils/FileUtils.kt
@@ -143,35 +143,37 @@ class FileUtils {
}
suspend fun getFilePath(context: Context, uri: Uri, overrideExisting: Boolean): String? {
- val name: String = getNameFromUri(uri, context)
- try {
- if (Os.fstat(
- ParcelFileDescriptor.open(
- File(uri.path),
- ParcelFileDescriptor.MODE_READ_ONLY
- ).fileDescriptor
- ).st_uid != Process.myUid()
- ) {
- Log.e("$TAG File descriptor UID different from our, denying copy!")
- return null
+ return withContext(Dispatchers.IO) {
+ val name: String = getNameFromUri(uri, context)
+ try {
+ if (Os.fstat(
+ ParcelFileDescriptor.open(
+ File(uri.path),
+ ParcelFileDescriptor.MODE_READ_ONLY
+ ).fileDescriptor
+ ).st_uid != Process.myUid()
+ ) {
+ Log.e("$TAG File descriptor UID different from our, denying copy!")
+ return@withContext null
+ }
+ } catch (e: Exception) {
+ Log.e("$TAG Can't check file ownership: ", e)
}
- } catch (e: Exception) {
- Log.e("$TAG Can't check file ownership: ", e)
+
+ val extension = getExtensionFromFileName(name)
+ val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
+ val isImage = getMimeType(type) == MimeType.Image
+
+ try {
+ val localFile: File = getFileStoragePath(name, isImage, overrideExisting)
+ copyFile(uri, localFile)
+ return@withContext localFile.absolutePath
+ } catch (e: Exception) {
+ Log.e("$TAG Can't copy file in local storage: ", e)
+ }
+
+ return@withContext null
}
-
- val extension = getExtensionFromFileName(name)
- val type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
- val isImage = getMimeType(type) == MimeType.Image
-
- try {
- val localFile: File = getFileStoragePath(name, isImage, overrideExisting)
- copyFile(uri, localFile)
- return localFile.absolutePath
- } catch (e: Exception) {
- Log.e("$TAG Can't copy file in local storage: ", e)
- }
-
- return null
}
@AnyThread
diff --git a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt
index 52f4a1118..38561be26 100644
--- a/app/src/main/java/org/linphone/utils/LinphoneUtils.kt
+++ b/app/src/main/java/org/linphone/utils/LinphoneUtils.kt
@@ -43,6 +43,7 @@ class LinphoneUtils {
private const val TAG = "[Linphone Utils]"
private const val RECORDING_DATE_PATTERN = "dd-MM-yyyy-HH-mm-ss"
+ private const val CHAT_ROOM_ID_SEPARATOR = "~"
@WorkerThread
fun getDefaultAccount(): Account? {
@@ -226,7 +227,23 @@ class LinphoneUtils {
@AnyThread
fun getChatRoomId(localSipUri: String, remoteSipUri: String): String {
- return "$localSipUri~$remoteSipUri"
+ return "$localSipUri$CHAT_ROOM_ID_SEPARATOR$remoteSipUri"
+ }
+
+ @AnyThread
+ fun getLocalAndPeerSipUrisFromChatRoomId(id: String): Pair? {
+ val split = id.split(CHAT_ROOM_ID_SEPARATOR)
+ if (split.size == 2) {
+ val localAddress = split[0]
+ val peerAddress = split[1]
+ Log.i(
+ "$TAG Got local [$localAddress] and peer [$peerAddress] SIP URIs from chat room id [$id]"
+ )
+ return Pair(localAddress, peerAddress)
+ } else {
+ Log.e("$TAG Failed to parse chat room id [$id]")
+ }
+ return null
}
@WorkerThread
diff --git a/app/src/main/res/xml/locales_config.xml b/app/src/main/res/xml/locales_config.xml
new file mode 100644
index 000000000..2f9a67e1c
--- /dev/null
+++ b/app/src/main/res/xml/locales_config.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file