mirror of
https://gitlab.linphone.org/BC/public/linphone-android.git
synced 2026-01-17 11:28:06 +00:00
Let user try to open file as plain text if no app on the device is registered to handle MIME type (useful for logs file without extension)
This commit is contained in:
parent
375c020b9b
commit
e85c97837f
10 changed files with 225 additions and 22 deletions
|
|
@ -84,6 +84,14 @@ class FileViewerActivity : GenericActivity() {
|
|||
finish()
|
||||
}
|
||||
|
||||
viewModel.showRedToastEvent.observe(this) {
|
||||
it.consume { pair ->
|
||||
val message = getString(pair.first)
|
||||
val icon = pair.second
|
||||
showRedToast(message, icon)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.fileReadyEvent.observe(this) {
|
||||
it.consume { done ->
|
||||
if (!done) {
|
||||
|
|
|
|||
|
|
@ -138,6 +138,10 @@ class FileViewModel
|
|||
Log.d("$TAG File [$file] seems to be plain text")
|
||||
loadPlainText()
|
||||
}
|
||||
FileUtils.MimeType.Unknown -> {
|
||||
Log.w("$TAG Unknown MIME type for file at [$file], opening it as plain text")
|
||||
loadPlainText()
|
||||
}
|
||||
else -> {
|
||||
Log.e("$TAG Unexpected MIME type [$mimeType] for file at [$file] with extension [$extension]")
|
||||
fileReadyEvent.value = Event(false)
|
||||
|
|
@ -333,6 +337,14 @@ class FileViewModel
|
|||
// TODO FIXME : improve performances !
|
||||
} catch (e: Exception) {
|
||||
Log.e("$TAG Exception trying to read file [$filePath] as text: $e")
|
||||
showRedToastEvent.postValue(
|
||||
Event(
|
||||
Pair(
|
||||
R.string.conversation_file_cant_be_opened_error_toast,
|
||||
R.drawable.warning_circle
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,14 +31,14 @@ import androidx.lifecycle.ViewModelProvider
|
|||
import androidx.navigation.fragment.findNavController
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import org.linphone.R
|
||||
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.ConfirmationDialogModel
|
||||
import org.linphone.utils.DialogUtils
|
||||
import org.linphone.utils.Event
|
||||
import org.linphone.utils.FileUtils
|
||||
import org.linphone.utils.RecyclerViewHeaderDecoration
|
||||
|
|
@ -149,10 +149,10 @@ class ConversationDocumentsListFragment : SlidingPaneChildFragment() {
|
|||
putBoolean("isEncrypted", fileModel.isEncrypted)
|
||||
putLong("timestamp", fileModel.fileCreationTimestamp)
|
||||
putString("originalPath", fileModel.originalPath)
|
||||
putBoolean("isMedia", false)
|
||||
}
|
||||
when (FileUtils.getMimeType(mime)) {
|
||||
FileUtils.MimeType.Pdf, FileUtils.MimeType.PlainText -> {
|
||||
bundle.putBoolean("isMedia", false)
|
||||
sharedViewModel.displayFileEvent.value = Event(bundle)
|
||||
}
|
||||
else -> {
|
||||
|
|
@ -165,13 +165,38 @@ class ConversationDocumentsListFragment : SlidingPaneChildFragment() {
|
|||
requireContext().startActivity(intent)
|
||||
} catch (anfe: ActivityNotFoundException) {
|
||||
Log.e("$TAG Can't open file [$path] in third party app: $anfe")
|
||||
val message = getString(
|
||||
R.string.conversation_no_app_registered_to_handle_content_type_error_toast
|
||||
)
|
||||
val icon = R.drawable.file
|
||||
(requireActivity() as GenericActivity).showRedToast(message, icon)
|
||||
showOpenAsPlainTextDialog(bundle)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showOpenAsPlainTextDialog(bundle: Bundle) {
|
||||
val model = ConfirmationDialogModel()
|
||||
val dialog = DialogUtils.getOpenAsPlainTextDialog(
|
||||
requireActivity(),
|
||||
model
|
||||
)
|
||||
|
||||
model.dismissEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
model.cancelEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
model.confirmEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
sharedViewModel.displayFileEvent.value = Event(bundle)
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@ import org.linphone.core.tools.Log
|
|||
import org.linphone.databinding.ChatConversationFragmentBinding
|
||||
import org.linphone.databinding.ChatConversationPopupMenuBinding
|
||||
import org.linphone.ui.GenericActivity
|
||||
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
|
||||
|
|
@ -1093,7 +1092,11 @@ open class ConversationFragment : SlidingPaneChildFragment() {
|
|||
val extension = FileUtils.getExtensionFromFileName(path)
|
||||
val mime = FileUtils.getMimeTypeFromExtension(extension)
|
||||
val mimeType = FileUtils.getMimeType(mime)
|
||||
Log.i("$TAG Extension for file [$path] is [$extension], associated MIME type is [$mimeType]")
|
||||
if (mimeType == FileUtils.MimeType.Unknown && extension.contains("/")) {
|
||||
Log.w("$TAG Slash character found in 'extension' [$extension] deduced from file path [$path]; MIME type will be Unknown")
|
||||
} else {
|
||||
Log.i("$TAG Extension for file [$path] is [$extension], associated MIME type is [$mimeType]")
|
||||
}
|
||||
|
||||
val bundle = Bundle()
|
||||
bundle.apply {
|
||||
|
|
@ -1114,7 +1117,8 @@ open class ConversationFragment : SlidingPaneChildFragment() {
|
|||
sharedViewModel.displayFileEvent.value = Event(bundle)
|
||||
}
|
||||
else -> {
|
||||
showOpenOrExportFileDialog(path, mime)
|
||||
bundle.putBoolean("isMedia", false)
|
||||
showOpenOrExportFileDialog(path, mime, bundle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1413,7 +1417,7 @@ open class ConversationFragment : SlidingPaneChildFragment() {
|
|||
bottomSheetDialog = unsafeConversationDetailsBottomSheet
|
||||
}
|
||||
|
||||
private fun showOpenOrExportFileDialog(path: String, mime: String) {
|
||||
private fun showOpenOrExportFileDialog(path: String, mime: String, bundle: Bundle) {
|
||||
val model = ConfirmationDialogModel()
|
||||
val dialog = DialogUtils.getOpenOrExportFileDialog(
|
||||
requireActivity(),
|
||||
|
|
@ -1428,7 +1432,7 @@ open class ConversationFragment : SlidingPaneChildFragment() {
|
|||
|
||||
model.cancelEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
openFileInAnotherApp(path, mime)
|
||||
openFileInAnotherApp(path, mime, bundle)
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
|
@ -1466,7 +1470,7 @@ open class ConversationFragment : SlidingPaneChildFragment() {
|
|||
dialog.show()
|
||||
}
|
||||
|
||||
private fun openFileInAnotherApp(path: String, mime: String) {
|
||||
private fun openFileInAnotherApp(path: String, mime: String, bundle: Bundle) {
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
val contentUri: Uri =
|
||||
FileUtils.getPublicFilePath(requireContext(), path)
|
||||
|
|
@ -1477,14 +1481,39 @@ open class ConversationFragment : SlidingPaneChildFragment() {
|
|||
requireContext().startActivity(intent)
|
||||
} catch (anfe: ActivityNotFoundException) {
|
||||
Log.e("$TAG Can't open file [$path] in third party app: $anfe")
|
||||
val message = getString(
|
||||
R.string.conversation_no_app_registered_to_handle_content_type_error_toast
|
||||
)
|
||||
val icon = R.drawable.file
|
||||
(requireActivity() as MainActivity).showRedToast(message, icon)
|
||||
showOpenAsPlainTextDialog(bundle)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showOpenAsPlainTextDialog(bundle: Bundle) {
|
||||
val model = ConfirmationDialogModel()
|
||||
val dialog = DialogUtils.getOpenAsPlainTextDialog(
|
||||
requireActivity(),
|
||||
model
|
||||
)
|
||||
|
||||
model.dismissEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
model.cancelEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
model.confirmEvent.observe(viewLifecycleOwner) {
|
||||
it.consume {
|
||||
sharedViewModel.displayFileEvent.value = Event(bundle)
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
private fun exportFile(path: String, mime: String) {
|
||||
filePathToExport = path
|
||||
|
||||
|
|
|
|||
|
|
@ -178,10 +178,10 @@ class ConversationMediaListFragment : SlidingPaneChildFragment() {
|
|||
putBoolean("isEncrypted", fileModel.isEncrypted)
|
||||
putLong("timestamp", fileModel.fileCreationTimestamp)
|
||||
putString("originalPath", fileModel.originalPath)
|
||||
putBoolean("isMedia", true)
|
||||
}
|
||||
when (FileUtils.getMimeType(mime)) {
|
||||
FileUtils.MimeType.Image, FileUtils.MimeType.Video, FileUtils.MimeType.Audio -> {
|
||||
bundle.putBoolean("isMedia", true)
|
||||
sharedViewModel.displayFileEvent.value = Event(bundle)
|
||||
}
|
||||
else -> {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ import org.linphone.databinding.DialogKickFromConferenceBinding
|
|||
import org.linphone.databinding.DialogManageAccountInternationalPrefixHelpBinding
|
||||
import org.linphone.databinding.DialogMergeCallsIntoConferenceBinding
|
||||
import org.linphone.databinding.DialogOpenExportFileBinding
|
||||
import org.linphone.databinding.DialogOpenPlainTextBinding
|
||||
import org.linphone.databinding.DialogPickNumberOrAddressBinding
|
||||
import org.linphone.databinding.DialogRemoveAccountBinding
|
||||
import org.linphone.databinding.DialogRemoveAllCallLogsBinding
|
||||
|
|
@ -343,6 +344,22 @@ class DialogUtils {
|
|||
return getDialog(context, binding)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun getOpenAsPlainTextDialog(
|
||||
context: Context,
|
||||
viewModel: ConfirmationDialogModel
|
||||
): Dialog {
|
||||
val binding: DialogOpenPlainTextBinding = DataBindingUtil.inflate(
|
||||
LayoutInflater.from(context),
|
||||
R.layout.dialog_open_plain_text,
|
||||
null,
|
||||
false
|
||||
)
|
||||
binding.viewModel = viewModel
|
||||
|
||||
return getDialog(context, binding)
|
||||
}
|
||||
|
||||
@UiThread
|
||||
fun getUpdateAvailableDialog(
|
||||
context: Context,
|
||||
|
|
|
|||
|
|
@ -115,8 +115,9 @@ class FileUtils {
|
|||
type.endsWith("/log") -> MimeType.PlainText
|
||||
type.startsWith("video/") -> MimeType.Video
|
||||
type.startsWith("audio/") -> MimeType.Audio
|
||||
type.startsWith("application/pdf") -> MimeType.Pdf
|
||||
type.startsWith("application/json") -> MimeType.PlainText
|
||||
type == "application/pdf" -> MimeType.Pdf
|
||||
type == "application/json" -> MimeType.PlainText
|
||||
type == "application/xml" -> MimeType.PlainText
|
||||
else -> MimeType.Unknown
|
||||
}
|
||||
Log.d("$TAG MIME type for [$type] is [$mime]")
|
||||
|
|
|
|||
103
app/src/main/res/layout/dialog_open_plain_text.xml
Normal file
103
app/src/main/res/layout/dialog_open_plain_text.xml
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
<variable
|
||||
name="viewModel"
|
||||
type="org.linphone.utils.ConfirmationDialogModel" />
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:onClick="@{() -> viewModel.dismiss()}"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/dialog_background"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginEnd="12dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:src="@drawable/shape_dialog_background"
|
||||
android:contentDescription="@null"
|
||||
app:layout_constraintWidth_max="@dimen/dialog_max_width"
|
||||
app:layout_constraintBottom_toBottomOf="@id/anchor"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/title" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/section_header_style"
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:paddingTop="@dimen/dialog_top_bottom_margin"
|
||||
android:text="@string/conversation_no_app_registered_to_handle_content_type_dialog_title"
|
||||
app:layout_constraintVertical_chainStyle="packed"
|
||||
app:layout_constraintBottom_toTopOf="@id/message"
|
||||
app:layout_constraintStart_toStartOf="@id/dialog_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/dialog_background"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
style="@style/default_text_style"
|
||||
android:id="@+id/message"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="@string/conversation_no_app_registered_to_handle_content_type_dialog_message"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toTopOf="@id/cancel"
|
||||
app:layout_constraintStart_toStartOf="@id/dialog_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/dialog_background"
|
||||
app:layout_constraintTop_toBottomOf="@id/title" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:onClick="@{() -> viewModel.cancel()}"
|
||||
style="@style/secondary_button_label_style"
|
||||
android:id="@+id/cancel"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="32dp"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:text="@string/dialog_cancel"
|
||||
app:layout_constraintStart_toStartOf="@id/dialog_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/dialog_background"
|
||||
app:layout_constraintTop_toBottomOf="@id/message"
|
||||
app:layout_constraintBottom_toTopOf="@id/confirm"/>
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:onClick="@{() -> viewModel.confirm()}"
|
||||
style="@style/primary_button_label_style"
|
||||
android:id="@+id/confirm"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginStart="15dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:text="@string/conversation_dialog_open_plain_text_label"
|
||||
app:layout_constraintStart_toStartOf="@id/dialog_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/dialog_background"
|
||||
app:layout_constraintTop_toBottomOf="@id/cancel"
|
||||
app:layout_constraintBottom_toTopOf="@id/anchor"/>
|
||||
|
||||
<View
|
||||
android:id="@+id/anchor"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/dialog_top_bottom_margin"
|
||||
app:layout_constraintTop_toBottomOf="@id/confirm"
|
||||
app:layout_constraintStart_toStartOf="@id/dialog_background"
|
||||
app:layout_constraintEnd_toEndOf="@id/dialog_background"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</layout>
|
||||
|
|
@ -463,6 +463,9 @@
|
|||
<string name="conversation_dialog_open_or_export_file_message">&appName; ne peut ouvrir ce fichier.\n\nVoulez-vous l\'ouvrir dans une autre app (si possible), ou le sauvegarder sur votre appareil ?</string>
|
||||
<string name="conversation_dialog_open_file_label">Ouvrir le fichier</string>
|
||||
<string name="conversation_dialog_export_file_label">Sauvegarder le fichier</string>
|
||||
<string name="conversation_no_app_registered_to_handle_content_type_dialog_title">Ouvrir comme texte brut ?</string>
|
||||
<string name="conversation_no_app_registered_to_handle_content_type_dialog_message">Aucune application trouvée pour lire ce fichier.\n\nVoulez-vous essayer de l\'ouvrir en tant que texte brut ?</string>
|
||||
<string name="conversation_dialog_open_plain_text_label">Ouvrir comme texte brut</string>
|
||||
<string name="conversation_failed_to_play_voice_recording_message">Impossible de lire le message vocal !</string>
|
||||
<string name="conversation_message_deleted_toast">Message supprimé</string>
|
||||
<string name="conversation_failed_to_create_toast">Échec de création de la conversation !</string>
|
||||
|
|
@ -483,6 +486,7 @@
|
|||
<string name="conversation_take_picture_label">Prendre une photo</string>
|
||||
<string name="conversation_pick_file_from_gallery_label">Ouvrir la gallerie</string>
|
||||
<string name="conversation_pick_any_file_label">Choisir un fichier</string>
|
||||
<string name="conversation_file_cant_be_opened_error_toast">Impossible d\'ouvrir le fichier!</string>
|
||||
|
||||
<string name="conversation_info_participants_list_title">Participants (%s)</string>
|
||||
<string name="conversation_info_add_participants_label">Ajouter des participants</string>
|
||||
|
|
|
|||
|
|
@ -503,6 +503,9 @@
|
|||
<string name="conversation_dialog_open_or_export_file_message">&appName; can\'t open this file.\n\nDo you want to open it in another app (if possible), or export it on your device?</string>
|
||||
<string name="conversation_dialog_open_file_label">Open file</string>
|
||||
<string name="conversation_dialog_export_file_label">Export file</string>
|
||||
<string name="conversation_no_app_registered_to_handle_content_type_dialog_title">Open as plain text?</string>
|
||||
<string name="conversation_no_app_registered_to_handle_content_type_dialog_message">No app found to open this kind of file.\n\nWould you like to try opening it as plain text?</string>
|
||||
<string name="conversation_dialog_open_plain_text_label">Open as plain text</string>
|
||||
<string name="conversation_failed_to_play_voice_recording_message">Voice recording cannot be played!</string>
|
||||
<string name="conversation_message_deleted_toast">Message has been deleted</string>
|
||||
<string name="conversation_failed_to_create_toast">Failed to create conversation!</string>
|
||||
|
|
@ -523,6 +526,7 @@
|
|||
<string name="conversation_take_picture_label">Take picture</string>
|
||||
<string name="conversation_pick_file_from_gallery_label">Open gallery</string>
|
||||
<string name="conversation_pick_any_file_label">Pick file</string>
|
||||
<string name="conversation_file_cant_be_opened_error_toast">File can\'t be opened!</string>
|
||||
|
||||
<string name="conversation_info_participants_list_title">Group members (%s)</string>
|
||||
<string name="conversation_info_add_participants_label">Add participants</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue