diff --git a/.gitignore b/.gitignore
index db3afa4ff..19acc0284 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.idea/deploymentTargetDropDown.xml
+.idea/deploymentTargetSelector.xml
.DS_Store
/build
/captures
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
deleted file mode 100644
index b268ef36c..000000000
--- a/.idea/deploymentTargetSelector.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 5ed8e2571..725570566 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -104,6 +104,16 @@
+
+
+
+
.
- */
-package org.linphone.ui.main.file_media_viewer.fragment
+package org.linphone.ui.file_viewer
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.DisplayMetrics
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
import androidx.annotation.UiThread
import androidx.core.content.FileProvider
-import androidx.core.view.doOnPreDraw
+import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
-import androidx.navigation.fragment.findNavController
-import androidx.navigation.fragment.navArgs
-import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
+import androidx.viewpager2.widget.ViewPager2
import java.io.File
import kotlinx.coroutines.launch
import org.linphone.R
import org.linphone.core.tools.Log
-import org.linphone.databinding.FileViewerFragmentBinding
-import org.linphone.ui.main.file_media_viewer.adapter.PdfPagesListAdapter
-import org.linphone.ui.main.file_media_viewer.viewmodel.FileViewModel
-import org.linphone.ui.main.fragment.GenericMainFragment
+import org.linphone.databinding.FileViewerActivityBinding
+import org.linphone.ui.GenericActivity
+import org.linphone.ui.file_viewer.adapter.PdfPagesListAdapter
+import org.linphone.ui.file_viewer.viewmodel.FileViewModel
import org.linphone.utils.FileUtils
@UiThread
-class FileViewerFragment : GenericMainFragment() {
+class FileViewerActivity : GenericActivity() {
companion object {
- private const val TAG = "[File Viewer Fragment]"
+ private const val TAG = "[File Viewer Activity]"
private const val EXPORT_FILE_AS_DOCUMENT = 10
}
- private lateinit var binding: FileViewerFragmentBinding
+ private lateinit var binding: FileViewerActivityBinding
private lateinit var viewModel: FileViewModel
private lateinit var adapter: PdfPagesListAdapter
- private val args: FileViewerFragmentArgs by navArgs()
-
- private val pageChangedListener = object : OnPageChangeCallback() {
+ private val pageChangedListener = object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
viewModel.pdfCurrentPage.value = (position + 1).toString()
}
}
- private var navBarDefaultColor: Int = -1
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ window.navigationBarColor = getColor(R.color.gray_900)
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- binding = FileViewerFragmentBinding.inflate(layoutInflater)
- return binding.root
- }
-
- override fun goBack(): Boolean {
- return findNavController().popBackStack()
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- postponeEnterTransition()
- super.onViewCreated(view, savedInstanceState)
-
- navBarDefaultColor = requireActivity().window.navigationBarColor
+ binding = DataBindingUtil.setContentView(this, R.layout.file_viewer_activity)
+ binding.lifecycleOwner = this
+ setUpToastsArea(binding.toastsArea)
viewModel = ViewModelProvider(this)[FileViewModel::class.java]
-
- binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
- observeToastEvents(viewModel)
- val path = args.path
- val preLoadedContent = args.content
+ val args = intent.extras
+ val path = args?.getString("path")
+ if (path.isNullOrEmpty()) {
+ finish()
+ return
+ }
+
+ val preLoadedContent = args.getString("content")
Log.i(
"$TAG Path argument is [$path], pre loaded text content is ${if (preLoadedContent.isNullOrEmpty()) "not available" else "available, using it"}"
)
viewModel.loadFile(path, preLoadedContent)
binding.setBackClickListener {
- goBack()
+ finish()
}
- viewModel.fileReadyEvent.observe(viewLifecycleOwner) {
+ viewModel.fileReadyEvent.observe(this) {
it.consume { done ->
- if (done) {
- (view.parent as? ViewGroup)?.doOnPreDraw {
- startPostponedEnterTransition()
- }
- } else {
- (view.parent as? ViewGroup)?.doOnPreDraw {
- Log.e("$TAG Failed to open file, going back")
- goBack()
- }
+ if (!done) {
+ finish()
+ Log.e("$TAG Failed to open file, going back")
}
}
}
@@ -124,7 +82,7 @@ class FileViewerFragment : GenericMainFragment() {
shareFile()
}
- viewModel.pdfRendererReadyEvent.observe(viewLifecycleOwner) {
+ viewModel.pdfRendererReadyEvent.observe(this) {
it.consume {
Log.i("$TAG PDF renderer is ready, attaching adapter to ViewPager")
if (viewModel.screenWidth == 0 || viewModel.screenHeight == 0) {
@@ -136,7 +94,7 @@ class FileViewerFragment : GenericMainFragment() {
}
}
- viewModel.exportPlainTextFileEvent.observe(viewLifecycleOwner) {
+ viewModel.exportPlainTextFileEvent.observe(this) {
it.consume { name ->
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
@@ -147,7 +105,7 @@ class FileViewerFragment : GenericMainFragment() {
}
}
- viewModel.exportPdfEvent.observe(viewLifecycleOwner) {
+ viewModel.exportPdfEvent.observe(this) {
it.consume { name ->
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
@@ -160,9 +118,6 @@ class FileViewerFragment : GenericMainFragment() {
}
override fun onResume() {
- // Force this navigation bar color
- requireActivity().window.navigationBarColor = requireContext().getColor(R.color.gray_900)
-
super.onResume()
updateScreenSize()
@@ -174,13 +129,6 @@ class FileViewerFragment : GenericMainFragment() {
super.onPause()
}
- override fun onDestroy() {
- // Reset default navigation bar color
- requireActivity().window.navigationBarColor = navBarDefaultColor
-
- super.onDestroy()
- }
-
@Deprecated("Deprecated in Java")
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == EXPORT_FILE_AS_DOCUMENT && resultCode == Activity.RESULT_OK) {
@@ -194,7 +142,7 @@ class FileViewerFragment : GenericMainFragment() {
private fun updateScreenSize() {
val displayMetrics = DisplayMetrics()
- requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
+ windowManager.defaultDisplay.getMetrics(displayMetrics)
viewModel.screenHeight = displayMetrics.heightPixels
viewModel.screenWidth = displayMetrics.widthPixels
Log.i(
@@ -206,15 +154,15 @@ class FileViewerFragment : GenericMainFragment() {
lifecycleScope.launch {
val filePath = FileUtils.getProperFilePath(viewModel.getFilePath())
val copy = FileUtils.getFilePath(
- requireContext(),
+ baseContext,
Uri.parse(filePath),
overrideExisting = true,
copyToCache = true
)
if (!copy.isNullOrEmpty()) {
val publicUri = FileProvider.getUriForFile(
- requireContext(),
- requireContext().getString(R.string.file_provider),
+ baseContext,
+ getString(R.string.file_provider),
File(copy)
)
Log.i("$TAG Public URI for file is [$publicUri], starting intent chooser")
diff --git a/app/src/main/java/org/linphone/ui/file_viewer/MediaViewerActivity.kt b/app/src/main/java/org/linphone/ui/file_viewer/MediaViewerActivity.kt
new file mode 100644
index 000000000..ece75a774
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/file_viewer/MediaViewerActivity.kt
@@ -0,0 +1,252 @@
+package org.linphone.ui.file_viewer
+
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import androidx.annotation.UiThread
+import androidx.core.content.FileProvider
+import androidx.databinding.DataBindingUtil
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.lifecycleScope
+import androidx.viewpager2.widget.ViewPager2
+import java.io.File
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+import org.linphone.R
+import org.linphone.core.tools.Log
+import org.linphone.databinding.FileMediaViewerActivityBinding
+import org.linphone.ui.GenericActivity
+import org.linphone.ui.file_viewer.adapter.MediaListAdapter
+import org.linphone.ui.file_viewer.viewmodel.MediaListViewModel
+import org.linphone.ui.main.chat.model.FileModel
+import org.linphone.utils.AppUtils
+import org.linphone.utils.FileUtils
+
+@UiThread
+class MediaViewerActivity : GenericActivity() {
+ companion object {
+ private const val TAG = "[Media Viewer Activity]"
+ }
+
+ private lateinit var binding: FileMediaViewerActivityBinding
+
+ private lateinit var adapter: MediaListAdapter
+
+ private lateinit var viewModel: MediaListViewModel
+
+ private lateinit var viewPager: ViewPager2
+
+ private val pageListener = object : ViewPager2.OnPageChangeCallback() {
+ override fun onPageSelected(position: Int) {
+ val list = viewModel.mediaList.value.orEmpty()
+ if (position >= 0 && position < list.size) {
+ val model = list[position]
+ viewModel.currentlyDisplayedFileName.value = "${model.fileName}\n${model.dateTime}"
+ }
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ window.navigationBarColor = getColor(R.color.gray_900)
+
+ binding = DataBindingUtil.setContentView(this, R.layout.file_media_viewer_activity)
+ binding.lifecycleOwner = this
+ setUpToastsArea(binding.toastsArea)
+
+ viewModel = ViewModelProvider(this)[MediaListViewModel::class.java]
+ binding.viewModel = viewModel
+
+ adapter = MediaListAdapter(this, viewModel) { fullScreen ->
+ viewModel.fullScreenMode.value = fullScreen
+ }
+
+ viewPager = binding.mediaViewPager
+ viewPager.adapter = adapter
+ viewPager.registerOnPageChangeCallback(pageListener)
+
+ val args = intent.extras
+ if (args == null) {
+ finish()
+ return
+ }
+
+ val path = args.getString("path", "")
+ if (path.isNullOrEmpty()) {
+ finish()
+ return
+ }
+
+ val timestamp = args.getLong("timestamp", -1)
+ val isEncrypted = args.getBoolean("isEncrypted", false)
+ val originalPath = args.getString("originalPath", "")
+ viewModel.initTempModel(path, timestamp, isEncrypted, originalPath)
+
+ val localSipUri = args.getString("localSipUri").orEmpty()
+ val remoteSipUri = args.getString("remoteSipUri").orEmpty()
+ Log.i(
+ "$TAG Looking up for conversation with local SIP URI [$localSipUri] and remote SIP URI [$remoteSipUri] trying to display file [$path]"
+ )
+ viewModel.findChatRoom(null, localSipUri, remoteSipUri)
+
+ viewModel.mediaList.observe(this) {
+ updateMediaList(path, it)
+ }
+
+ binding.setBackClickListener {
+ finish()
+ }
+
+ binding.setShareClickListener {
+ shareFile()
+ }
+
+ binding.setExportClickListener {
+ exportFile()
+ }
+ }
+
+ override fun onDestroy() {
+ if (::viewPager.isInitialized) {
+ viewPager.unregisterOnPageChangeCallback(pageListener)
+ }
+
+ super.onDestroy()
+ }
+
+ private fun updateMediaList(path: String, list: List) {
+ val count = list.size
+ Log.i("$TAG Found [$count] media for conversation")
+
+ var index = list.indexOfFirst { model ->
+ model.path == path
+ }
+
+ if (index == -1) {
+ Log.d(
+ "$TAG Path [$path] not found in media list (expected if VFS is enabled), trying using file name"
+ )
+ val fileName = FileUtils.getNameFromFilePath(path)
+ val underscore = fileName.indexOf("_")
+ val originalFileName = if (underscore != -1 && underscore < 2) {
+ fileName.subSequence(underscore, fileName.length)
+ } else {
+ fileName
+ }
+ index = list.indexOfFirst { model ->
+ model.path.endsWith(originalFileName)
+ }
+ if (index == -1) {
+ Log.w(
+ "$TAG Path [$path] not found in media list using either path or filename [$originalFileName]"
+ )
+ }
+ }
+
+ val position = if (index == -1) {
+ Log.e(
+ "$TAG File [$path] not found, using most recent one instead!"
+ )
+ val message = getString(R.string.conversation_media_not_found_toast)
+ val icon = R.drawable.warning_circle
+ showRedToast(message, icon)
+
+ 0
+ } else {
+ index
+ }
+
+ viewPager.setCurrentItem(position, false)
+ viewPager.offscreenPageLimit = 1
+ }
+
+ private fun exportFile() {
+ val list = viewModel.mediaList.value.orEmpty()
+ val currentItem = binding.mediaViewPager.currentItem
+ val model = if (currentItem >= 0 && currentItem < list.size) list[currentItem] else null
+ if (model != null) {
+ val filePath = model.path
+ lifecycleScope.launch {
+ withContext(Dispatchers.IO) {
+ Log.i(
+ "$TAG Export file [$filePath] to Android's MediaStore"
+ )
+ val mediaStorePath = FileUtils.addContentToMediaStore(filePath)
+ if (mediaStorePath.isNotEmpty()) {
+ Log.i(
+ "$TAG File [$filePath] has been successfully exported to MediaStore"
+ )
+ val message = AppUtils.getString(
+ R.string.toast_file_successfully_exported_to_media_store
+ )
+ showGreenToast(
+ message,
+ R.drawable.check
+ )
+ } else {
+ Log.e(
+ "$TAG Failed to export file [$filePath] to MediaStore!"
+ )
+ val message = AppUtils.getString(
+ R.string.toast_export_file_to_media_store_error
+ )
+ showRedToast(
+ message,
+ R.drawable.warning_circle
+ )
+ }
+ }
+ }
+ } else {
+ Log.e(
+ "$TAG Failed to get FileModel at index [$currentItem], only [${list.size}] items in list"
+ )
+ }
+ }
+
+ private fun shareFile() {
+ val list = viewModel.mediaList.value.orEmpty()
+ val currentItem = binding.mediaViewPager.currentItem
+ val model = if (currentItem >= 0 && currentItem < list.size) list[currentItem] else null
+ if (model != null) {
+ lifecycleScope.launch {
+ val filePath = FileUtils.getProperFilePath(model.path)
+ val copy = FileUtils.getFilePath(
+ baseContext,
+ Uri.parse(filePath),
+ overrideExisting = true,
+ copyToCache = true
+ )
+ if (!copy.isNullOrEmpty()) {
+ val publicUri = FileProvider.getUriForFile(
+ baseContext,
+ getString(R.string.file_provider),
+ File(copy)
+ )
+ Log.i(
+ "$TAG Public URI for file is [$publicUri], starting intent chooser"
+ )
+
+ val sendIntent: Intent = Intent().apply {
+ action = Intent.ACTION_SEND
+ putExtra(Intent.EXTRA_STREAM, publicUri)
+ putExtra(Intent.EXTRA_SUBJECT, model.fileName)
+ type = model.mimeTypeString
+ }
+
+ val shareIntent = Intent.createChooser(sendIntent, null)
+ startActivity(shareIntent)
+ } else {
+ Log.e(
+ "$TAG Failed to copy file [$filePath] to share!"
+ )
+ }
+ }
+ } else {
+ Log.e(
+ "$TAG Failed to get FileModel at index [$currentItem], only [${list.size}] items in list"
+ )
+ }
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/main/file_media_viewer/adapter/MediaListAdapter.kt b/app/src/main/java/org/linphone/ui/file_viewer/adapter/MediaListAdapter.kt
similarity index 59%
rename from app/src/main/java/org/linphone/ui/main/file_media_viewer/adapter/MediaListAdapter.kt
rename to app/src/main/java/org/linphone/ui/file_viewer/adapter/MediaListAdapter.kt
index 1872f9fc1..d5c28dd89 100644
--- a/app/src/main/java/org/linphone/ui/main/file_media_viewer/adapter/MediaListAdapter.kt
+++ b/app/src/main/java/org/linphone/ui/file_viewer/adapter/MediaListAdapter.kt
@@ -17,18 +17,22 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.linphone.ui.main.file_media_viewer.adapter
+package org.linphone.ui.file_viewer.adapter
import android.os.Bundle
import androidx.fragment.app.Fragment
+import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import org.linphone.core.tools.Log
-import org.linphone.ui.main.chat.viewmodel.ConversationMediaListViewModel
-import org.linphone.ui.main.file_media_viewer.fragment.MediaViewerFragment
+import org.linphone.ui.file_viewer.fragment.MediaViewerFragment
+import org.linphone.ui.file_viewer.viewmodel.MediaListViewModel
-class MediaListAdapter(fragment: Fragment, private val viewModel: ConversationMediaListViewModel) : FragmentStateAdapter(
- fragment
-) {
+class MediaListAdapter(
+ fragmentActivity: FragmentActivity,
+ private val viewModel: MediaListViewModel,
+ private val lambda: ((fullScreen: Boolean) -> Unit)
+) :
+ FragmentStateAdapter(fragmentActivity) {
companion object {
private const val TAG = "[Media List Adapter]"
}
@@ -37,12 +41,22 @@ class MediaListAdapter(fragment: Fragment, private val viewModel: ConversationMe
return viewModel.mediaList.value.orEmpty().size
}
+ override fun getItemId(position: Int): Long {
+ return viewModel.mediaList.value.orEmpty().getOrNull(position)?.originalPath.hashCode().toLong()
+ }
+
+ override fun containsItem(itemId: Long): Boolean {
+ return viewModel.mediaList.value.orEmpty().any { it.originalPath.hashCode().toLong() == itemId }
+ }
+
override fun createFragment(position: Int): Fragment {
val fragment = MediaViewerFragment()
+ fragment.fullScreenChanged = lambda
fragment.arguments = Bundle().apply {
- val path = viewModel.mediaList.value.orEmpty().getOrNull(position)?.file
- Log.i("$TAG Path is [$path] for position [$position]")
+ val path = viewModel.mediaList.value.orEmpty().getOrNull(position)?.path
+ Log.d("$TAG Path is [$path] for position [$position]")
putString("path", path)
+ putBoolean("fullScreen", viewModel.fullScreenMode.value == true)
}
return fragment
}
diff --git a/app/src/main/java/org/linphone/ui/main/file_media_viewer/adapter/PdfPagesListAdapter.kt b/app/src/main/java/org/linphone/ui/file_viewer/adapter/PdfPagesListAdapter.kt
similarity index 94%
rename from app/src/main/java/org/linphone/ui/main/file_media_viewer/adapter/PdfPagesListAdapter.kt
rename to app/src/main/java/org/linphone/ui/file_viewer/adapter/PdfPagesListAdapter.kt
index b960e8ba0..b61ed5b10 100644
--- a/app/src/main/java/org/linphone/ui/main/file_media_viewer/adapter/PdfPagesListAdapter.kt
+++ b/app/src/main/java/org/linphone/ui/file_viewer/adapter/PdfPagesListAdapter.kt
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.linphone.ui.main.file_media_viewer.adapter
+package org.linphone.ui.file_viewer.adapter
import android.view.LayoutInflater
import android.view.View
@@ -25,7 +25,7 @@ import android.view.ViewGroup
import android.widget.ImageView
import androidx.recyclerview.widget.RecyclerView
import org.linphone.R
-import org.linphone.ui.main.file_media_viewer.viewmodel.FileViewModel
+import org.linphone.ui.file_viewer.viewmodel.FileViewModel
class PdfPagesListAdapter(private val viewModel: FileViewModel) : RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PdfPageViewHolder {
diff --git a/app/src/main/java/org/linphone/ui/main/file_media_viewer/fragment/MediaViewerFragment.kt b/app/src/main/java/org/linphone/ui/file_viewer/fragment/MediaViewerFragment.kt
similarity index 87%
rename from app/src/main/java/org/linphone/ui/main/file_media_viewer/fragment/MediaViewerFragment.kt
rename to app/src/main/java/org/linphone/ui/file_viewer/fragment/MediaViewerFragment.kt
index 4784116f3..d635eee83 100644
--- a/app/src/main/java/org/linphone/ui/main/file_media_viewer/fragment/MediaViewerFragment.kt
+++ b/app/src/main/java/org/linphone/ui/file_viewer/fragment/MediaViewerFragment.kt
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.linphone.ui.main.file_media_viewer.fragment
+package org.linphone.ui.file_viewer.fragment
import android.os.Bundle
import android.view.LayoutInflater
@@ -27,9 +27,10 @@ import androidx.annotation.UiThread
import androidx.lifecycle.ViewModelProvider
import org.linphone.core.tools.Log
import org.linphone.databinding.FileMediaViewerChildFragmentBinding
-import org.linphone.ui.main.file_media_viewer.viewmodel.MediaViewModel
+import org.linphone.ui.file_viewer.viewmodel.MediaViewModel
import org.linphone.ui.main.fragment.GenericMainFragment
import org.linphone.ui.main.viewmodel.SharedMainViewModel
+import org.linphone.utils.FileUtils
@UiThread
class MediaViewerFragment : GenericMainFragment() {
@@ -41,6 +42,8 @@ class MediaViewerFragment : GenericMainFragment() {
private lateinit var viewModel: MediaViewModel
+ var fullScreenChanged: ((fullScreen: Boolean) -> Unit)? = null
+
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
@@ -58,6 +61,7 @@ class MediaViewerFragment : GenericMainFragment() {
}
viewModel = ViewModelProvider(this)[MediaViewModel::class.java]
+ viewModel.fullScreenMode.value = arguments?.getBoolean("fullScreen", true) ?: true
binding.lifecycleOwner = viewLifecycleOwner
binding.viewModel = viewModel
@@ -73,7 +77,8 @@ class MediaViewerFragment : GenericMainFragment() {
return
}
- Log.i("$TAG Path argument is [$path]")
+ val exists = FileUtils.doesFileExist(path)
+ Log.i("$TAG Path argument is [$path], it ${if (exists) "exists" else "doesn't exist"}")
viewModel.loadFile(path)
viewModel.isVideo.observe(viewLifecycleOwner) { isVideo ->
@@ -94,18 +99,15 @@ class MediaViewerFragment : GenericMainFragment() {
}
}
- viewModel.fullScreenMode.observe(viewLifecycleOwner) {
- if (it != sharedViewModel.mediaViewerFullScreenMode.value) {
- sharedViewModel.mediaViewerFullScreenMode.value = it
- }
+ binding.setToggleFullScreenModeClickListener {
+ viewModel.toggleFullScreen()
+ fullScreenChanged?.invoke(viewModel.fullScreenMode.value == true)
}
}
override fun onResume() {
super.onResume()
- viewModel.fullScreenMode.value = sharedViewModel.mediaViewerFullScreenMode.value
-
if (viewModel.isVideo.value == true) {
Log.i("$TAG Resumed, starting video player")
binding.videoPlayer.start()
diff --git a/app/src/main/java/org/linphone/ui/main/file_media_viewer/viewmodel/FileViewModel.kt b/app/src/main/java/org/linphone/ui/file_viewer/viewmodel/FileViewModel.kt
similarity index 97%
rename from app/src/main/java/org/linphone/ui/main/file_media_viewer/viewmodel/FileViewModel.kt
rename to app/src/main/java/org/linphone/ui/file_viewer/viewmodel/FileViewModel.kt
index 19d3e419c..1d3d0e219 100644
--- a/app/src/main/java/org/linphone/ui/main/file_media_viewer/viewmodel/FileViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/file_viewer/viewmodel/FileViewModel.kt
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.linphone.ui.main.file_media_viewer.viewmodel
+package org.linphone.ui.file_viewer.viewmodel
import android.graphics.Bitmap
import android.graphics.pdf.PdfRenderer
@@ -119,14 +119,15 @@ class FileViewModel @UiThread constructor() : GenericViewModel() {
mimeType.postValue(mime)
when (FileUtils.getMimeType(mime)) {
FileUtils.MimeType.Pdf -> {
- Log.i("$TAG File [$file] seems to be a PDF")
+ Log.d("$TAG File [$file] seems to be a PDF")
loadPdf()
}
FileUtils.MimeType.PlainText -> {
- Log.i("$TAG File [$file] seems to be plain text")
+ Log.d("$TAG File [$file] seems to be plain text")
loadPlainText()
}
else -> {
+ Log.e("$TAG Unexpected MIME type [$mime] for file at [$file]")
fileReadyEvent.value = Event(false)
}
}
@@ -158,7 +159,7 @@ class FileViewModel @UiThread constructor() : GenericViewModel() {
val page: PdfRenderer.Page = pdfRenderer.openPage(index)
currentPdfPage = page
- Log.i(
+ Log.d(
"$TAG Page size is ${page.width}/${page.height}, screen size is $screenWidth/$screenHeight"
)
val bm = Bitmap.createBitmap(
diff --git a/app/src/main/java/org/linphone/ui/file_viewer/viewmodel/MediaListViewModel.kt b/app/src/main/java/org/linphone/ui/file_viewer/viewmodel/MediaListViewModel.kt
new file mode 100644
index 000000000..61ca1baa4
--- /dev/null
+++ b/app/src/main/java/org/linphone/ui/file_viewer/viewmodel/MediaListViewModel.kt
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2010-2023 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 .
+ */
+package org.linphone.ui.file_viewer.viewmodel
+
+import androidx.annotation.UiThread
+import androidx.annotation.WorkerThread
+import androidx.lifecycle.MutableLiveData
+import org.linphone.core.tools.Log
+import org.linphone.ui.main.chat.model.FileModel
+import org.linphone.ui.main.chat.viewmodel.AbstractConversationViewModel
+import org.linphone.utils.FileUtils
+import org.linphone.utils.LinphoneUtils
+
+class MediaListViewModel @UiThread constructor() : AbstractConversationViewModel() {
+ companion object {
+ private const val TAG = "[Media List ViewModel]"
+ }
+
+ val mediaList = MutableLiveData>()
+
+ val fullScreenMode = MutableLiveData()
+
+ val currentlyDisplayedFileName = MutableLiveData()
+
+ override fun beforeNotifyingChatRoomFound(sameOne: Boolean) {
+ loadMediaList()
+ }
+
+ init {
+ fullScreenMode.value = true
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+
+ mediaList.value.orEmpty().forEach(FileModel::destroy)
+ }
+
+ @UiThread
+ fun initTempModel(path: String, timestamp: Long, isEncrypted: Boolean, originalPath: String) {
+ val name = FileUtils.getNameFromFilePath(path)
+ val model = FileModel(path, name, 0, timestamp, isEncrypted, originalPath)
+ mediaList.postValue(arrayListOf(model))
+ }
+
+ @WorkerThread
+ private fun loadMediaList() {
+ val list = arrayListOf()
+ val chatRoomId = LinphoneUtils.getChatRoomId(chatRoom)
+ Log.i("$TAG Loading media contents for conversation [$chatRoomId]")
+
+ val media = chatRoom.mediaContents
+ Log.i("$TAG [${media.size}] media have been fetched")
+ for (mediaContent in media) {
+ // Do not display voice recordings here, even if they are media file
+ if (mediaContent.isVoiceRecording) continue
+
+ val isEncrypted = mediaContent.isFileEncrypted
+ val originalPath = mediaContent.filePath.orEmpty()
+ val path = if (isEncrypted) {
+ Log.d(
+ "$TAG [VFS] Content is encrypted, requesting plain file path for file [${mediaContent.filePath}]"
+ )
+ mediaContent.exportPlainFile()
+ } else {
+ originalPath
+ }
+ val name = mediaContent.name.orEmpty()
+ val size = mediaContent.size.toLong()
+ val timestamp = mediaContent.creationTimestamp
+ if (path.isNotEmpty() && name.isNotEmpty()) {
+ val model = FileModel(path, name, size, timestamp, isEncrypted, originalPath)
+ list.add(model)
+ }
+ }
+ Log.i("$TAG [${list.size}] media have been processed")
+ mediaList.postValue(list)
+ }
+}
diff --git a/app/src/main/java/org/linphone/ui/main/file_media_viewer/viewmodel/MediaViewModel.kt b/app/src/main/java/org/linphone/ui/file_viewer/viewmodel/MediaViewModel.kt
similarity index 92%
rename from app/src/main/java/org/linphone/ui/main/file_media_viewer/viewmodel/MediaViewModel.kt
rename to app/src/main/java/org/linphone/ui/file_viewer/viewmodel/MediaViewModel.kt
index 8339c8901..9c2286fec 100644
--- a/app/src/main/java/org/linphone/ui/main/file_media_viewer/viewmodel/MediaViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/file_viewer/viewmodel/MediaViewModel.kt
@@ -17,7 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.linphone.ui.main.file_media_viewer.viewmodel
+package org.linphone.ui.file_viewer.viewmodel
import android.media.AudioAttributes
import android.media.MediaPlayer
@@ -75,22 +75,24 @@ class MediaViewModel @UiThread constructor() : GenericViewModel() {
val mime = FileUtils.getMimeTypeFromExtension(extension)
when (FileUtils.getMimeType(mime)) {
FileUtils.MimeType.Image -> {
- Log.i("$TAG File [$file] seems to be an image")
+ Log.d("$TAG File [$file] seems to be an image")
isImage.value = true
path.value = file
}
FileUtils.MimeType.Video -> {
- Log.i("$TAG File [$file] seems to be a video")
+ Log.d("$TAG File [$file] seems to be a video")
isVideo.value = true
isVideoPlaying.value = false
}
FileUtils.MimeType.Audio -> {
- Log.i("$TAG File [$file] seems to be an audio file")
+ Log.d("$TAG File [$file] seems to be an audio file")
isAudio.value = true
initMediaPlayer()
}
- else -> { }
+ else -> {
+ Log.e("$TAG Unexpected MIME type [$mime] for file at [$file]")
+ }
}
}
diff --git a/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationsFilesAdapter.kt b/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationsFilesAdapter.kt
index 9c9082a74..298d7af90 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationsFilesAdapter.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/adapter/ConversationsFilesAdapter.kt
@@ -136,7 +136,7 @@ class ConversationsFilesAdapter :
private class FilesDiffCallback : DiffUtil.ItemCallback() {
override fun areItemsTheSame(oldItem: FileModel, newItem: FileModel): Boolean {
- return oldItem.file == newItem.file && oldItem.fileName == newItem.fileName
+ return oldItem.path == newItem.path && oldItem.fileName == newItem.fileName
}
override fun areContentsTheSame(oldItem: FileModel, newItem: FileModel): Boolean {
diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationDocumentsListFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationDocumentsListFragment.kt
index 7420ca524..516ea89c1 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationDocumentsListFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationDocumentsListFragment.kt
@@ -37,6 +37,7 @@ import org.linphone.core.tools.Log
import org.linphone.databinding.ChatDocumentsFragmentBinding
import org.linphone.ui.GenericActivity
import org.linphone.ui.main.chat.adapter.ConversationsFilesAdapter
+import org.linphone.ui.main.chat.model.FileModel
import org.linphone.ui.main.chat.viewmodel.ConversationDocumentsListViewModel
import org.linphone.ui.main.fragment.SlidingPaneChildFragment
import org.linphone.utils.Event
@@ -126,13 +127,14 @@ class ConversationDocumentsListFragment : SlidingPaneChildFragment() {
viewModel.openDocumentEvent.observe(viewLifecycleOwner) {
it.consume { model ->
- Log.i("$TAG User clicked on file [${model.file}], let's display it in file viewer")
- goToFileViewer(model.file)
+ Log.i("$TAG User clicked on file [${model.path}], let's display it in file viewer")
+ goToFileViewer(model)
}
}
}
- private fun goToFileViewer(path: String) {
+ private fun goToFileViewer(fileModel: FileModel) {
+ val path = fileModel.path
Log.i("$TAG Navigating to file viewer fragment with path [$path]")
val extension = FileUtils.getExtensionFromFileName(path)
val mime = FileUtils.getMimeTypeFromExtension(extension)
@@ -142,6 +144,9 @@ class ConversationDocumentsListFragment : SlidingPaneChildFragment() {
putString("localSipUri", viewModel.localSipUri)
putString("remoteSipUri", viewModel.remoteSipUri)
putString("path", path)
+ putBoolean("isEncrypted", fileModel.isEncrypted)
+ putLong("timestamp", fileModel.fileCreationTimestamp)
+ putString("originalPath", fileModel.originalPath)
}
when (FileUtils.getMimeType(mime)) {
FileUtils.MimeType.Pdf, FileUtils.MimeType.PlainText -> {
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 a51cd6705..f2093ef71 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
@@ -80,6 +80,7 @@ import org.linphone.ui.main.MainActivity
import org.linphone.ui.main.chat.ConversationScrollListener
import org.linphone.ui.main.chat.adapter.ConversationEventAdapter
import org.linphone.ui.main.chat.adapter.MessageBottomSheetAdapter
+import org.linphone.ui.main.chat.model.FileModel
import org.linphone.ui.main.chat.model.MessageDeliveryModel
import org.linphone.ui.main.chat.model.MessageModel
import org.linphone.ui.main.chat.model.MessageReactionsModel
@@ -614,10 +615,10 @@ class ConversationFragment : SlidingPaneChildFragment() {
}
viewModel.fileToDisplayEvent.observe(viewLifecycleOwner) {
- it.consume { file ->
+ it.consume { model ->
if (messageLongPressDialog != null) return@consume
- Log.i("$TAG User clicked on file [$file], let's display it in file viewer")
- goToFileViewer(file)
+ Log.i("$TAG User clicked on file [${model.path}], let's display it in file viewer")
+ goToFileViewer(model)
}
}
@@ -892,7 +893,8 @@ class ConversationFragment : SlidingPaneChildFragment() {
}
}
- private fun goToFileViewer(path: String) {
+ private fun goToFileViewer(fileModel: FileModel) {
+ val path = fileModel.path
Log.i("$TAG Navigating to file viewer fragment with path [$path]")
val extension = FileUtils.getExtensionFromFileName(path)
val mime = FileUtils.getMimeTypeFromExtension(extension)
@@ -902,6 +904,9 @@ class ConversationFragment : SlidingPaneChildFragment() {
putString("localSipUri", viewModel.localSipUri)
putString("remoteSipUri", viewModel.remoteSipUri)
putString("path", path)
+ putBoolean("isEncrypted", fileModel.isEncrypted)
+ putLong("timestamp", fileModel.fileCreationTimestamp)
+ putString("originalPath", fileModel.originalPath)
}
when (FileUtils.getMimeType(mime)) {
FileUtils.MimeType.Image, FileUtils.MimeType.Video, FileUtils.MimeType.Audio -> {
diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationMediaListFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationMediaListFragment.kt
index 7917d06e7..c87e79cd3 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationMediaListFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationMediaListFragment.kt
@@ -38,6 +38,7 @@ import org.linphone.core.tools.Log
import org.linphone.databinding.ChatMediaFragmentBinding
import org.linphone.ui.GenericActivity
import org.linphone.ui.main.chat.adapter.ConversationsFilesAdapter
+import org.linphone.ui.main.chat.model.FileModel
import org.linphone.ui.main.chat.viewmodel.ConversationMediaListViewModel
import org.linphone.ui.main.fragment.SlidingPaneChildFragment
import org.linphone.utils.Event
@@ -83,7 +84,9 @@ class ConversationMediaListFragment : SlidingPaneChildFragment() {
binding.lifecycleOwner = viewLifecycleOwner
- viewModel = ViewModelProvider(this)[ConversationMediaListViewModel::class.java]
+ if (!::viewModel.isInitialized) {
+ viewModel = ViewModelProvider(this)[ConversationMediaListViewModel::class.java]
+ }
binding.viewModel = viewModel
observeToastEvents(viewModel)
@@ -126,8 +129,6 @@ class ConversationMediaListFragment : SlidingPaneChildFragment() {
return 1
}
}
- // This isn't supported by GridLayoutManager, it will crash
- // layoutManager.stackFromEnd = true
binding.mediaList.layoutManager = layoutManager
if (binding.mediaList.adapter != adapter) {
@@ -155,13 +156,14 @@ class ConversationMediaListFragment : SlidingPaneChildFragment() {
viewModel.openMediaEvent.observe(viewLifecycleOwner) {
it.consume { model ->
- Log.i("$TAG User clicked on file [${model.file}], let's display it in file viewer")
- goToFileViewer(model.file)
+ Log.i("$TAG User clicked on file [${model.path}], let's display it in file viewer")
+ goToFileViewer(model)
}
}
}
- private fun goToFileViewer(path: String) {
+ private fun goToFileViewer(fileModel: FileModel) {
+ val path = fileModel.path
Log.i("$TAG Navigating to file viewer fragment with path [$path]")
val extension = FileUtils.getExtensionFromFileName(path)
val mime = FileUtils.getMimeTypeFromExtension(extension)
@@ -171,6 +173,9 @@ class ConversationMediaListFragment : SlidingPaneChildFragment() {
putString("localSipUri", viewModel.localSipUri)
putString("remoteSipUri", viewModel.remoteSipUri)
putString("path", path)
+ putBoolean("isEncrypted", fileModel.isEncrypted)
+ putLong("timestamp", fileModel.fileCreationTimestamp)
+ putString("originalPath", fileModel.originalPath)
}
when (FileUtils.getMimeType(mime)) {
FileUtils.MimeType.Image, FileUtils.MimeType.Video, FileUtils.MimeType.Audio -> {
diff --git a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt
index f5f4e8958..7bf1cd67d 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/fragment/ConversationsListFragment.kt
@@ -19,6 +19,7 @@
*/
package org.linphone.ui.main.chat.fragment
+import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@@ -36,6 +37,8 @@ import org.linphone.R
import org.linphone.core.tools.Log
import org.linphone.databinding.ChatListFragmentBinding
import org.linphone.ui.GenericActivity
+import org.linphone.ui.file_viewer.FileViewerActivity
+import org.linphone.ui.file_viewer.MediaViewerActivity
import org.linphone.ui.main.chat.adapter.ConversationsListAdapter
import org.linphone.ui.main.chat.viewmodel.ConversationsListViewModel
import org.linphone.ui.main.fragment.AbstractMainFragment
@@ -85,9 +88,7 @@ class ConversationsListFragment : AbstractMainFragment() {
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
if (
findNavController().currentDestination?.id == R.id.startConversationFragment ||
- findNavController().currentDestination?.id == R.id.meetingWaitingRoomFragment ||
- findNavController().currentDestination?.id == R.id.fileViewerFragment ||
- findNavController().currentDestination?.id == R.id.mediaListViewerFragment
+ findNavController().currentDestination?.id == R.id.meetingWaitingRoomFragment
) {
// Holds fragment in place while new fragment slides over it
return AnimationUtils.loadAnimation(activity, R.anim.hold)
@@ -232,24 +233,23 @@ class ConversationsListFragment : AbstractMainFragment() {
if (findNavController().currentDestination?.id == R.id.conversationsListFragment) {
val path = bundle.getString("path", "")
val isMedia = bundle.getBoolean("isMedia", false)
+ if (path.isEmpty()) {
+ Log.e("$TAG Can't navigate to file viewer for empty path!")
+ return@consume
+ }
+
Log.i(
"$TAG Navigating to ${if (isMedia) "media" else "file"} viewer fragment with path [$path]"
)
- val action = if (isMedia) {
- val localSipUri = bundle.getString("localSipUri", "")
- val remoteSipUri = bundle.getString("remoteSipUri", "")
- ConversationsListFragmentDirections.actionConversationsListFragmentToMediaListViewerFragment(
- localSipUri = localSipUri,
- remoteSipUri = remoteSipUri,
- path = path
- )
+ if (isMedia) {
+ val intent = Intent(requireActivity(), MediaViewerActivity::class.java)
+ intent.putExtras(bundle)
+ startActivity(intent)
} else {
- ConversationsListFragmentDirections.actionConversationsListFragmentToFileViewerFragment(
- path,
- null
- )
+ val intent = Intent(requireActivity(), FileViewerActivity::class.java)
+ intent.putExtras(bundle)
+ startActivity(intent)
}
- findNavController().navigate(action)
}
}
}
diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt
index f53bbbdd1..4d21226e0 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/model/EventLogModel.kt
@@ -32,7 +32,7 @@ class EventLogModel @WorkerThread constructor(
isFromGroup: Boolean = false,
isGroupedWithPreviousOne: Boolean = false,
isGroupedWithNextOne: Boolean = false,
- onContentClicked: ((file: String) -> Unit)? = null,
+ onContentClicked: ((fileModel: FileModel) -> Unit)? = null,
onJoinConferenceClicked: ((uri: String) -> Unit)? = null,
onWebUrlClicked: ((url: String) -> Unit)? = null,
onContactClicked: ((friendRefKey: String) -> Unit)? = null,
diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/FileModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/FileModel.kt
index 06a307d6f..3b432a058 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/model/FileModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/model/FileModel.kt
@@ -35,11 +35,12 @@ import org.linphone.utils.FileUtils
import org.linphone.utils.TimestampUtils
class FileModel @AnyThread constructor(
- val file: String,
+ val path: String,
val fileName: String,
val fileSize: Long,
- private val fileCreationTimestamp: Long,
- private val isEncrypted: Boolean,
+ val fileCreationTimestamp: Long,
+ val isEncrypted: Boolean,
+ val originalPath: String,
val isWaitingToBeDownloaded: Boolean = false,
private val onClicked: ((model: FileModel) -> Unit)? = null
) {
@@ -82,7 +83,7 @@ class FileModel @AnyThread constructor(
formattedFileSize.postValue(FileUtils.bytesToDisplayableSize(fileSize))
if (!isWaitingToBeDownloaded) {
- val extension = FileUtils.getExtensionFromFileName(file)
+ val extension = FileUtils.getExtensionFromFileName(path)
isPdf = extension == "pdf"
val mime = FileUtils.getMimeTypeFromExtension(extension)
@@ -113,9 +114,9 @@ class FileModel @AnyThread constructor(
@AnyThread
fun destroy() {
if (isEncrypted) {
- Log.i("$TAG [VFS] Deleting plain file in cache: $file")
+ Log.i("$TAG [VFS] Deleting plain file in cache: $path")
scope.launch {
- FileUtils.deleteFile(file)
+ FileUtils.deleteFile(path)
}
}
}
@@ -127,22 +128,22 @@ class FileModel @AnyThread constructor(
@AnyThread
suspend fun deleteFile() {
- Log.i("$TAG Deleting file [$file]")
- FileUtils.deleteFile(file)
+ Log.i("$TAG Deleting file [$path]")
+ FileUtils.deleteFile(path)
}
private fun getDuration() {
try {
val retriever = MediaMetadataRetriever()
- retriever.setDataSource(coreContext.context, Uri.parse(file))
+ retriever.setDataSource(coreContext.context, Uri.parse(path))
val durationInMs = retriever.extractMetadata(METADATA_KEY_DURATION)?.toInt() ?: 0
val seconds = durationInMs / 1000
val duration = TimestampUtils.durationToString(seconds)
- Log.d("$TAG Duration for file [$file] is $duration")
+ Log.d("$TAG Duration for file [$path] is $duration")
audioVideoDuration.postValue(duration)
retriever.release()
} catch (e: Exception) {
- Log.e("$TAG Failed to get duration for file [$file]: $e")
+ Log.e("$TAG Failed to get duration for file [$path]: $e")
}
}
}
diff --git a/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt b/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt
index dc3bb6d27..2d26e6a68 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/model/MessageModel.kt
@@ -73,7 +73,7 @@ class MessageModel @WorkerThread constructor(
val isForward: Boolean,
isGroupedWithPreviousOne: Boolean,
isGroupedWithNextOne: Boolean,
- private val onContentClicked: ((file: String) -> Unit)? = null,
+ private val onContentClicked: ((fileModel: FileModel) -> Unit)? = null,
private val onJoinConferenceClicked: ((uri: String) -> Unit)? = null,
private val onWebUrlClicked: ((url: String) -> Unit)? = null,
private val onContactClicked: ((friendRefKey: String) -> Unit)? = null,
@@ -355,13 +355,14 @@ class MessageModel @WorkerThread constructor(
checkAndRepairFilePathIfNeeded(content)
+ val originalPath = content.filePath.orEmpty()
val path = if (isFileEncrypted) {
- Log.i(
+ Log.d(
"$TAG [VFS] Content is encrypted, requesting plain file path for file [${content.filePath}]"
)
content.exportPlainFile()
} else {
- content.filePath ?: ""
+ originalPath
}
val name = content.name ?: ""
if (path.isNotEmpty()) {
@@ -378,9 +379,10 @@ class MessageModel @WorkerThread constructor(
name,
fileSize,
timestamp,
- isFileEncrypted
+ isFileEncrypted,
+ originalPath
) { model ->
- onContentClicked?.invoke(model.file)
+ onContentClicked?.invoke(model)
}
filesPath.add(fileModel)
@@ -392,9 +394,10 @@ class MessageModel @WorkerThread constructor(
name,
fileSize,
timestamp,
- isFileEncrypted
+ isFileEncrypted,
+ originalPath
) { model ->
- onContentClicked?.invoke(model.file)
+ onContentClicked?.invoke(model)
}
filesPath.add(fileModel)
@@ -414,16 +417,17 @@ class MessageModel @WorkerThread constructor(
val timestamp = content.creationTimestamp
if (name.isNotEmpty()) {
val fileModel = if (isOutgoing && chatMessage.isFileTransferInProgress) {
- val path = content.filePath ?: ""
+ val path = content.filePath.orEmpty()
FileModel(
path,
name,
content.fileSize.toLong(),
timestamp,
isFileEncrypted,
+ path,
false
) { model ->
- onContentClicked?.invoke(model.file)
+ onContentClicked?.invoke(model)
}
} else {
FileModel(
@@ -432,6 +436,7 @@ class MessageModel @WorkerThread constructor(
content.fileSize.toLong(),
timestamp,
isFileEncrypted,
+ name,
true
) { model ->
downloadContent(model, content)
diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationDocumentsListViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationDocumentsListViewModel.kt
index fa974031b..7e4a99a0f 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationDocumentsListViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationDocumentsListViewModel.kt
@@ -45,6 +45,8 @@ class ConversationDocumentsListViewModel @UiThread constructor() : AbstractConve
}
override fun onCleared() {
+ super.onCleared()
+
documentsList.value.orEmpty().forEach(FileModel::destroy)
}
@@ -62,19 +64,20 @@ class ConversationDocumentsListViewModel @UiThread constructor() : AbstractConve
Log.i("$TAG [${documents.size}] documents have been fetched")
for (documentContent in documents) {
val isEncrypted = documentContent.isFileEncrypted
+ val originalPath = documentContent.filePath.orEmpty()
val path = if (isEncrypted) {
- Log.i(
+ Log.d(
"$TAG [VFS] Content is encrypted, requesting plain file path for file [${documentContent.filePath}]"
)
documentContent.exportPlainFile()
} else {
- documentContent.filePath.orEmpty()
+ originalPath
}
val name = documentContent.name.orEmpty()
val size = documentContent.size.toLong()
val timestamp = documentContent.creationTimestamp
if (path.isNotEmpty() && name.isNotEmpty()) {
- val model = FileModel(path, name, size, timestamp, isEncrypted) {
+ val model = FileModel(path, name, size, timestamp, isEncrypted, originalPath) {
openDocumentEvent.postValue(Event(it))
}
list.add(model)
diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationMediaListViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationMediaListViewModel.kt
index cb83fa496..9df1594b5 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationMediaListViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/ConversationMediaListViewModel.kt
@@ -34,12 +34,6 @@ class ConversationMediaListViewModel @UiThread constructor() : AbstractConversat
val mediaList = MutableLiveData>()
- val fullScreenMode = MutableLiveData()
-
- val currentlyDisplayedFileName = MutableLiveData()
-
- val operationInProgress = MutableLiveData()
-
val openMediaEvent: MutableLiveData> by lazy {
MutableLiveData>()
}
@@ -49,13 +43,13 @@ class ConversationMediaListViewModel @UiThread constructor() : AbstractConversat
}
override fun onCleared() {
+ super.onCleared()
+
mediaList.value.orEmpty().forEach(FileModel::destroy)
}
@WorkerThread
private fun loadMediaList() {
- operationInProgress.postValue(true)
-
val list = arrayListOf()
Log.i(
"$TAG Loading media contents for conversation [${LinphoneUtils.getChatRoomId(
@@ -69,19 +63,20 @@ class ConversationMediaListViewModel @UiThread constructor() : AbstractConversat
if (mediaContent.isVoiceRecording) continue
val isEncrypted = mediaContent.isFileEncrypted
+ val originalPath = mediaContent.filePath.orEmpty()
val path = if (isEncrypted) {
- Log.i(
+ Log.d(
"$TAG [VFS] Content is encrypted, requesting plain file path for file [${mediaContent.filePath}]"
)
mediaContent.exportPlainFile()
} else {
- mediaContent.filePath.orEmpty()
+ originalPath
}
val name = mediaContent.name.orEmpty()
val size = mediaContent.size.toLong()
val timestamp = mediaContent.creationTimestamp
if (path.isNotEmpty() && name.isNotEmpty()) {
- val model = FileModel(path, name, size, timestamp, isEncrypted) {
+ val model = FileModel(path, name, size, timestamp, isEncrypted, originalPath) {
openMediaEvent.postValue(Event(it))
}
list.add(model)
@@ -89,6 +84,5 @@ class ConversationMediaListViewModel @UiThread constructor() : AbstractConversat
}
Log.i("$TAG [${media.size}] media have been processed")
mediaList.postValue(list)
- operationInProgress.postValue(false)
}
}
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 7b1ea0ffa..e22aad72f 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
@@ -43,6 +43,7 @@ import org.linphone.core.Participant
import org.linphone.core.ParticipantInfo
import org.linphone.core.tools.Log
import org.linphone.ui.main.chat.model.EventLogModel
+import org.linphone.ui.main.chat.model.FileModel
import org.linphone.ui.main.chat.model.MessageModel
import org.linphone.ui.main.contacts.model.ContactAvatarModel
import org.linphone.ui.main.model.isEndToEndEncryptionMandatory
@@ -100,8 +101,8 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo
MutableLiveData>()
}
- val fileToDisplayEvent: MutableLiveData> by lazy {
- MutableLiveData>()
+ val fileToDisplayEvent: MutableLiveData> by lazy {
+ MutableLiveData>()
}
val conferenceToJoinEvent: MutableLiveData> by lazy {
@@ -664,8 +665,8 @@ class ConversationViewModel @UiThread constructor() : AbstractConversationViewMo
groupChatRoom,
index > 0,
index != groupedEventLogs.size - 1,
- { file ->
- fileToDisplayEvent.postValue(Event(file))
+ { fileModel ->
+ fileToDisplayEvent.postValue(Event(fileModel))
},
{ conferenceUri ->
conferenceToJoinEvent.postValue(Event(conferenceUri))
diff --git a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt
index 744237ab1..a10f1be90 100644
--- a/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/chat/viewmodel/SendMessageInConversationViewModel.kt
@@ -264,7 +264,7 @@ class SendMessageInConversationViewModel @UiThread constructor() : GenericViewMo
}
content.name = attachment.fileName
// Let the file body handler take care of the upload
- content.filePath = attachment.file
+ content.filePath = attachment.path
message.addFileContent(content)
}
@@ -320,8 +320,8 @@ class SendMessageInConversationViewModel @UiThread constructor() : GenericViewMo
val fileName = FileUtils.getNameFromFilePath(file)
val timestamp = System.currentTimeMillis() / 1000
- val model = FileModel(file, fileName, 0, timestamp, isEncrypted = false) { model ->
- removeAttachment(model.file)
+ val model = FileModel(file, fileName, 0, timestamp, false, file) { model ->
+ removeAttachment(model.path)
}
list.add(model)
@@ -340,7 +340,7 @@ class SendMessageInConversationViewModel @UiThread constructor() : GenericViewMo
val list = arrayListOf()
list.addAll(attachments.value.orEmpty())
val found = list.find {
- it.file == file
+ it.path == file
}
if (found != null) {
if (delete) {
diff --git a/app/src/main/java/org/linphone/ui/main/file_media_viewer/fragment/MediaListViewerFragment.kt b/app/src/main/java/org/linphone/ui/main/file_media_viewer/fragment/MediaListViewerFragment.kt
deleted file mode 100644
index 03c0ccf7f..000000000
--- a/app/src/main/java/org/linphone/ui/main/file_media_viewer/fragment/MediaListViewerFragment.kt
+++ /dev/null
@@ -1,279 +0,0 @@
-/*
- * Copyright (c) 2010-2023 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 .
- */
-package org.linphone.ui.main.file_media_viewer.fragment
-
-import android.content.Intent
-import android.net.Uri
-import android.os.Bundle
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import androidx.core.content.FileProvider
-import androidx.core.view.doOnPreDraw
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.lifecycleScope
-import androidx.navigation.fragment.navArgs
-import androidx.viewpager2.widget.ViewPager2
-import java.io.File
-import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-import org.linphone.R
-import org.linphone.core.tools.Log
-import org.linphone.databinding.FileMediaViewerFragmentBinding
-import org.linphone.ui.GenericActivity
-import org.linphone.ui.main.chat.viewmodel.ConversationMediaListViewModel
-import org.linphone.ui.main.file_media_viewer.adapter.MediaListAdapter
-import org.linphone.ui.main.fragment.GenericMainFragment
-import org.linphone.utils.AppUtils
-import org.linphone.utils.FileUtils
-
-class MediaListViewerFragment : GenericMainFragment() {
- companion object {
- private const val TAG = "[Media List Viewer]"
- }
-
- private lateinit var binding: FileMediaViewerFragmentBinding
-
- private lateinit var adapter: MediaListAdapter
-
- private lateinit var viewModel: ConversationMediaListViewModel
-
- private lateinit var viewPager: ViewPager2
-
- private val args: MediaListViewerFragmentArgs by navArgs()
-
- private var navBarDefaultColor: Int = -1
-
- private val pageListener = object : ViewPager2.OnPageChangeCallback() {
- override fun onPageSelected(position: Int) {
- val list = viewModel.mediaList.value.orEmpty()
- if (position >= 0 && position < list.size) {
- val model = list[position]
- viewModel.currentlyDisplayedFileName.value = "${model.fileName}\n${model.dateTime}"
- }
- }
- }
-
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View {
- binding = FileMediaViewerFragmentBinding.inflate(layoutInflater)
- return binding.root
- }
-
- override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- postponeEnterTransition()
- super.onViewCreated(view, savedInstanceState)
-
- navBarDefaultColor = requireActivity().window.navigationBarColor
-
- binding.lifecycleOwner = viewLifecycleOwner
-
- viewModel = ViewModelProvider(this)[ConversationMediaListViewModel::class.java]
- binding.viewModel = viewModel
- observeToastEvents(viewModel)
-
- // Consider full screen mode the default
- sharedViewModel.mediaViewerFullScreenMode.value = true
-
- val localSipUri = args.localSipUri
- val remoteSipUri = args.remoteSipUri
- val path = args.path
- Log.i(
- "$TAG Looking up for conversation with local SIP URI [$localSipUri] and remote SIP URI [$remoteSipUri] trying to display file [$path]"
- )
- val chatRoom = sharedViewModel.displayedChatRoom
- viewModel.findChatRoom(chatRoom, localSipUri, remoteSipUri)
-
- viewModel.mediaList.observe(viewLifecycleOwner) {
- val count = it.size
- Log.i(
- "$TAG Found [$count] media for conversation with local SIP URI [$localSipUri] and remote SIP URI [$remoteSipUri]"
- )
- adapter = MediaListAdapter(this, viewModel)
- viewPager = binding.mediaViewPager
- viewPager.adapter = adapter
-
- viewPager.registerOnPageChangeCallback(pageListener)
-
- var index = it.indexOfFirst { model ->
- model.file == path
- }
-
- if (index == -1) {
- Log.i(
- "$TAG Path [$path] not found in media list (expected if VFS is enabled), trying using file name"
- )
- val fileName = File(path).name
- val underscore = fileName.indexOf("_")
- val originalFileName = if (underscore != -1 && underscore < 2) {
- fileName.subSequence(underscore, fileName.length)
- } else {
- fileName
- }
- index = it.indexOfFirst { model ->
- model.file.endsWith(originalFileName)
- }
- if (index == -1) {
- Log.w(
- "$TAG Path [$path] not found either using filename [$originalFileName] match"
- )
- }
- }
-
- val position = if (index == -1) {
- Log.e("$TAG File [$path] not found, using latest one available instead!")
- val message = getString(R.string.conversation_media_not_found_toast)
- val icon = R.drawable.warning_circle
- (requireActivity() as GenericActivity).showRedToast(message, icon)
-
- count - 1
- } else {
- index
- }
- viewPager.setCurrentItem(position, false)
- viewPager.offscreenPageLimit = 1
-
- (view.parent as? ViewGroup)?.doOnPreDraw {
- startPostponedEnterTransition()
- }
- }
-
- binding.setBackClickListener {
- goBack()
- }
-
- binding.setShareClickListener {
- shareFile()
- }
-
- binding.setExportClickListener {
- exportFile()
- }
-
- sharedViewModel.mediaViewerFullScreenMode.observe(viewLifecycleOwner) {
- if (it != viewModel.fullScreenMode.value) {
- viewModel.fullScreenMode.value = it
- }
- }
- }
-
- override fun onResume() {
- // Force this navigation bar color
- requireActivity().window.navigationBarColor = requireContext().getColor(R.color.gray_900)
-
- super.onResume()
- }
-
- override fun onDestroy() {
- // Reset default navigation bar color
- requireActivity().window.navigationBarColor = navBarDefaultColor
-
- if (::viewPager.isInitialized) {
- viewPager.unregisterOnPageChangeCallback(pageListener)
- }
-
- super.onDestroy()
- }
-
- private fun exportFile() {
- val list = viewModel.mediaList.value.orEmpty()
- val currentItem = binding.mediaViewPager.currentItem
- val model = if (currentItem >= 0 && currentItem < list.size) list[currentItem] else null
- if (model != null) {
- val filePath = model.file
- lifecycleScope.launch {
- withContext(Dispatchers.IO) {
- Log.i("$TAG Export file [$filePath] to Android's MediaStore")
- val mediaStorePath = FileUtils.addContentToMediaStore(filePath)
- if (mediaStorePath.isNotEmpty()) {
- Log.i(
- "$TAG File [$filePath] has been successfully exported to MediaStore"
- )
- val message = AppUtils.getString(
- R.string.toast_file_successfully_exported_to_media_store
- )
- (requireActivity() as GenericActivity).showGreenToast(
- message,
- R.drawable.check
- )
- } else {
- Log.e("$TAG Failed to export file [$filePath] to MediaStore!")
- val message = AppUtils.getString(
- R.string.toast_export_file_to_media_store_error
- )
- (requireActivity() as GenericActivity).showRedToast(
- message,
- R.drawable.warning_circle
- )
- }
- }
- }
- } else {
- Log.e(
- "$TAG Failed to get FileModel at index [$currentItem], only [${list.size}] items in list"
- )
- }
- }
-
- private fun shareFile() {
- val list = viewModel.mediaList.value.orEmpty()
- val currentItem = binding.mediaViewPager.currentItem
- val model = if (currentItem >= 0 && currentItem < list.size) list[currentItem] else null
- if (model != null) {
- lifecycleScope.launch {
- val filePath = FileUtils.getProperFilePath(model.file)
- val copy = FileUtils.getFilePath(
- requireContext(),
- Uri.parse(filePath),
- overrideExisting = true,
- copyToCache = true
- )
- if (!copy.isNullOrEmpty()) {
- val publicUri = FileProvider.getUriForFile(
- requireContext(),
- requireContext().getString(R.string.file_provider),
- File(copy)
- )
- Log.i("$TAG Public URI for file is [$publicUri], starting intent chooser")
-
- val sendIntent: Intent = Intent().apply {
- action = Intent.ACTION_SEND
- putExtra(Intent.EXTRA_STREAM, publicUri)
- putExtra(Intent.EXTRA_SUBJECT, model.fileName)
- type = model.mimeTypeString
- }
-
- val shareIntent = Intent.createChooser(sendIntent, null)
- startActivity(shareIntent)
- } else {
- Log.e("$TAG Failed to copy file [$filePath] to share!")
- }
- }
- } else {
- Log.e(
- "$TAG Failed to get FileModel at index [$currentItem], only [${list.size}] items in list"
- )
- }
- }
-}
diff --git a/app/src/main/java/org/linphone/ui/main/help/fragment/DebugFragment.kt b/app/src/main/java/org/linphone/ui/main/help/fragment/DebugFragment.kt
index 00c494e22..6ea890923 100644
--- a/app/src/main/java/org/linphone/ui/main/help/fragment/DebugFragment.kt
+++ b/app/src/main/java/org/linphone/ui/main/help/fragment/DebugFragment.kt
@@ -32,6 +32,7 @@ import org.linphone.core.CorePreferences
import org.linphone.core.tools.Log
import org.linphone.databinding.HelpDebugFragmentBinding
import org.linphone.ui.GenericActivity
+import org.linphone.ui.file_viewer.FileViewerActivity
import org.linphone.ui.main.fragment.GenericMainFragment
import org.linphone.ui.main.help.viewmodel.HelpViewModel
@@ -117,11 +118,12 @@ class DebugFragment : GenericMainFragment() {
viewModel.showConfigFileEvent.observe(viewLifecycleOwner) {
it.consume { content ->
if (findNavController().currentDestination?.id == R.id.debugFragment) {
- val action = DebugFragmentDirections.actionDebugFragmentToFileViewerFragment(
- CorePreferences.CONFIG_FILE_NAME,
- content
- )
- findNavController().navigate(action)
+ val intent = Intent(requireActivity(), FileViewerActivity::class.java)
+ val bundle = Bundle()
+ bundle.putString("path", CorePreferences.CONFIG_FILE_NAME)
+ bundle.putString("content", content)
+ intent.putExtras(bundle)
+ startActivity(intent)
}
}
}
diff --git a/app/src/main/java/org/linphone/ui/main/sso/viewmodel/SingleSignOnViewModel.kt b/app/src/main/java/org/linphone/ui/main/sso/viewmodel/SingleSignOnViewModel.kt
index a51956f29..d5649ea72 100644
--- a/app/src/main/java/org/linphone/ui/main/sso/viewmodel/SingleSignOnViewModel.kt
+++ b/app/src/main/java/org/linphone/ui/main/sso/viewmodel/SingleSignOnViewModel.kt
@@ -47,6 +47,7 @@ class SingleSignOnViewModel : GenericViewModel() {
private const val CLIENT_ID = "linphone"
private const val REDIRECT_URI = "org.linphone:/openidcallback"
+ private const val OPEN_ID_WELL_KNOWN = ".well-known/openid-configuration"
}
val singleSignOnProcessCompletedEvent = MutableLiveData>()
@@ -106,12 +107,12 @@ class SingleSignOnViewModel : GenericViewModel() {
Log.e(
"$TAG Failed to fetch configuration on [$singleSignOnUrl]: ${ex.errorDescription}"
)
- if (!singleSignOnUrl.endsWith(".well-known/openid-configuration")) {
- Log.w("$TAG Trying again appending .well-known/openid-configuration to URL")
+ if (!singleSignOnUrl.endsWith(OPEN_ID_WELL_KNOWN)) {
+ Log.w("$TAG Trying again appending [$OPEN_ID_WELL_KNOWN] to URL")
singleSignOnUrl = if (singleSignOnUrl.endsWith("/")) {
- "$singleSignOnUrl.well-known/openid-configuration"
+ "$singleSignOnUrl$OPEN_ID_WELL_KNOWN"
} else {
- "$singleSignOnUrl/.well-known/openid-configuration"
+ "$singleSignOnUrl/$OPEN_ID_WELL_KNOWN"
}
singleSignOn()
return@RetrieveConfigurationCallback
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 7be60a83b..ee27dd051 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
@@ -124,8 +124,6 @@ class SharedMainViewModel @UiThread constructor() : ViewModel() {
// When using keyboard to share gif or other, see RichContentReceiver & RichEditText classes
val richContentUri = MutableLiveData>()
- val mediaViewerFullScreenMode = MutableLiveData()
-
val displayFileEvent: 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 4fd215c94..52f97b0ce 100644
--- a/app/src/main/java/org/linphone/utils/FileUtils.kt
+++ b/app/src/main/java/org/linphone/utils/FileUtils.kt
@@ -118,7 +118,7 @@ class FileUtils {
type.startsWith("application/json") -> MimeType.PlainText
else -> MimeType.Unknown
}
- Log.i("$TAG MIME type for [$type] is [$mime]")
+ Log.d("$TAG MIME type for [$type] is [$mime]")
return mime
}
@@ -388,6 +388,11 @@ class FileUtils {
}
}
+ fun doesFileExist(path: String): Boolean {
+ val file = File(path)
+ return file.exists()
+ }
+
@AnyThread
suspend fun dumpStringToFile(data: String, to: File): Boolean {
try {
diff --git a/app/src/main/res/layout/chat_bubble_content_grid_cell.xml b/app/src/main/res/layout/chat_bubble_content_grid_cell.xml
index 5eb698d14..923445249 100644
--- a/app/src/main/res/layout/chat_bubble_content_grid_cell.xml
+++ b/app/src/main/res/layout/chat_bubble_content_grid_cell.xml
@@ -36,7 +36,7 @@
android:scaleType="centerCrop"
android:contentDescription="@{model.isVideoPreview ? @string/content_description_chat_bubble_video : @string/content_description_chat_bubble_image}"
android:visibility="@{model.isImage || model.isVideoPreview ? View.VISIBLE : View.GONE}"
- coilBubbleGrid="@{model.file}"
+ coilBubbleGrid="@{model.path}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
diff --git a/app/src/main/res/layout/chat_bubble_single_media_content.xml b/app/src/main/res/layout/chat_bubble_single_media_content.xml
index 2b1925f14..ceebd5ebd 100644
--- a/app/src/main/res/layout/chat_bubble_single_media_content.xml
+++ b/app/src/main/res/layout/chat_bubble_single_media_content.xml
@@ -30,7 +30,7 @@
android:adjustViewBounds="true"
android:scaleType="fitCenter"
android:contentDescription="@{model.isVideoPreview ? @string/content_description_chat_bubble_video : @string/content_description_chat_bubble_image}"
- coilBubble="@{model.file}"
+ coilBubble="@{model.path}"
app:layout_constraintHeight_max="@dimen/chat_bubble_big_image_max_size"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
diff --git a/app/src/main/res/layout/chat_media_content_grid_cell.xml b/app/src/main/res/layout/chat_media_content_grid_cell.xml
index df8a48aa8..37a69bb34 100644
--- a/app/src/main/res/layout/chat_media_content_grid_cell.xml
+++ b/app/src/main/res/layout/chat_media_content_grid_cell.xml
@@ -22,7 +22,7 @@
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:contentDescription="@{model.isVideoPreview ? @string/content_description_chat_bubble_video : @string/content_description_chat_bubble_image}"
- coilBubbleGrid="@{model.file}"
+ coilBubbleGrid="@{model.path}"
app:layout_constraintDimensionRatio="1:1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
diff --git a/app/src/main/res/layout/chat_media_fragment.xml b/app/src/main/res/layout/chat_media_fragment.xml
index a1442b4ab..dfdb3a55e 100644
--- a/app/src/main/res/layout/chat_media_fragment.xml
+++ b/app/src/main/res/layout/chat_media_fragment.xml
@@ -1,7 +1,6 @@
+ xmlns:app="http://schemas.android.com/apk/res-auto">
@@ -13,71 +12,61 @@
type="org.linphone.ui.main.chat.viewmodel.ConversationMediaListViewModel" />
-
+ android:layout_height="match_parent"
+ android:background="?attr/color_main2_000">
-
+
-
+
-
+
-
+
-
-
-
-
-
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/file_media_viewer_fragment.xml b/app/src/main/res/layout/file_media_viewer_activity.xml
similarity index 83%
rename from app/src/main/res/layout/file_media_viewer_fragment.xml
rename to app/src/main/res/layout/file_media_viewer_activity.xml
index f2150bbb6..b71fe337d 100644
--- a/app/src/main/res/layout/file_media_viewer_fragment.xml
+++ b/app/src/main/res/layout/file_media_viewer_activity.xml
@@ -15,7 +15,7 @@
type="View.OnClickListener" />
+ type="org.linphone.ui.file_viewer.viewmodel.MediaListViewModel" />
+
+
@@ -68,7 +72,6 @@
android:padding="15dp"
android:src="@drawable/share_network"
android:contentDescription="@string/content_description_share_file"
- android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
app:tint="?attr/color_main1_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toStartOf="@id/save"
@@ -84,7 +87,6 @@
android:padding="15dp"
android:src="@drawable/download_simple"
android:contentDescription="@string/content_description_save_file"
- android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
app:tint="?attr/color_main1_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toEndOf="parent"
@@ -102,11 +104,23 @@
android:textSize="12sp"
android:textColor="?attr/color_main2_600"
android:textAlignment="center"
- android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/file_media_viewer_child_fragment.xml b/app/src/main/res/layout/file_media_viewer_child_fragment.xml
index 3921a0af7..2c02babfa 100644
--- a/app/src/main/res/layout/file_media_viewer_child_fragment.xml
+++ b/app/src/main/res/layout/file_media_viewer_child_fragment.xml
@@ -5,13 +5,16 @@
+
+ type="org.linphone.ui.file_viewer.viewmodel.MediaViewModel" />
@@ -58,7 +61,7 @@
+ type="org.linphone.ui.file_viewer.viewmodel.FileViewModel" />
+
+
@@ -109,7 +113,6 @@
android:padding="15dp"
android:src="@drawable/share_network"
android:contentDescription="@string/content_description_share_file"
- android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
app:tint="?attr/color_main1_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toStartOf="@id/save"
@@ -125,7 +128,6 @@
android:padding="15dp"
android:src="@drawable/download_simple"
android:contentDescription="@string/content_description_save_file"
- android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
app:tint="?attr/color_main1_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toEndOf="parent"
@@ -143,11 +145,23 @@
android:textSize="12sp"
android:textColor="?attr/color_main2_600"
android:textAlignment="center"
- android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
+
+
\ No newline at end of file
diff --git a/app/src/main/res/navigation/main_nav_graph.xml b/app/src/main/res/navigation/main_nav_graph.xml
index 07d72949d..b44018cc9 100644
--- a/app/src/main/res/navigation/main_nav_graph.xml
+++ b/app/src/main/res/navigation/main_nav_graph.xml
@@ -216,12 +216,6 @@
app:launchSingleTop="true"
app:popUpTo="@id/helpFragment"
app:popUpToInclusive="true"/>
-
-
-
-
-
-
-
-
-
-
-
-
-
-