Reworked file viewer interface, improved video/audio player

This commit is contained in:
Sylvain Berfini 2024-05-17 11:20:03 +02:00
parent 26e30c6060
commit 376af91e88
13 changed files with 304 additions and 173 deletions

View file

@ -53,17 +53,23 @@ class FileViewerActivity : GenericActivity() {
binding.viewModel = viewModel
val args = intent.extras
val path = args?.getString("path")
if (args == null) {
finish()
return
}
val path = args.getString("path")
if (path.isNullOrEmpty()) {
finish()
return
}
val timestamp = args.getLong("timestamp", -1)
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)
viewModel.loadFile(path, timestamp, preLoadedContent)
binding.setBackClickListener {
finish()

View file

@ -42,7 +42,8 @@ class MediaViewerActivity : GenericActivity() {
val list = viewModel.mediaList.value.orEmpty()
if (position >= 0 && position < list.size) {
val model = list[position]
viewModel.currentlyDisplayedFileName.value = "${model.fileName}\n${model.dateTime}"
viewModel.currentlyDisplayedFileName.value = model.fileName
viewModel.currentlyDisplayedFileDateTime.value = model.dateTime
}
}
}

View file

@ -19,8 +19,11 @@
*/
package org.linphone.ui.file_viewer.fragment
import android.graphics.SurfaceTexture
import android.os.Bundle
import android.view.LayoutInflater
import android.view.Surface
import android.view.TextureView.SurfaceTextureListener
import android.view.View
import android.view.ViewGroup
import androidx.annotation.UiThread
@ -81,24 +84,6 @@ class MediaViewerFragment : GenericMainFragment() {
Log.i("$TAG Path argument is [$path], it ${if (exists) "exists" else "doesn't exist"}")
viewModel.loadFile(path)
viewModel.isVideo.observe(viewLifecycleOwner) { isVideo ->
if (isVideo) {
initVideoPlayer(path)
}
}
viewModel.toggleVideoPlayPauseEvent.observe(viewLifecycleOwner) {
it.consume { play ->
if (play) {
Log.i("$TAG Starting video player")
binding.videoPlayer.start()
} else {
Log.i("$TAG Pausing video player")
binding.videoPlayer.pause()
}
}
}
binding.setToggleFullScreenModeClickListener {
viewModel.toggleFullScreen()
fullScreenChanged?.invoke(viewModel.fullScreenMode.value == true)
@ -108,40 +93,47 @@ class MediaViewerFragment : GenericMainFragment() {
override fun onResume() {
super.onResume()
if (viewModel.isVideo.value == true) {
Log.i("$TAG Resumed, starting video player")
binding.videoPlayer.start()
viewModel.isVideoPlaying.value = true
val textureView = binding.videoPlayer
if (textureView.isAvailable) {
Log.i("$TAG Surface created, setting display in mediaPlayer")
viewModel.mediaPlayer.setSurface((Surface(textureView.surfaceTexture)))
} else {
Log.i("$TAG Surface not available yet, setting listener")
textureView.surfaceTextureListener = object : SurfaceTextureListener {
override fun onSurfaceTextureAvailable(
surfaceTexture: SurfaceTexture,
p1: Int,
p2: Int
) {
Log.i("$TAG Surface available, setting display in mediaPlayer")
viewModel.mediaPlayer.setSurface(Surface(surfaceTexture))
}
override fun onSurfaceTextureSizeChanged(
surfaceTexture: SurfaceTexture,
p1: Int,
p2: Int
) {
}
override fun onSurfaceTextureDestroyed(surfaceTexture: SurfaceTexture): Boolean {
return true
}
override fun onSurfaceTextureUpdated(surfaceTexture: SurfaceTexture) {
}
}
}
viewModel.play()
}
override fun onPause() {
if (binding.videoPlayer.isPlaying) {
Log.i("$TAG Paused, stopping video player")
binding.videoPlayer.pause()
viewModel.isVideoPlaying.value = false
}
if (viewModel.isAudioPlaying.value == true) {
Log.i("$TAG Paused, stopping audio player")
viewModel.pauseAudio()
if (viewModel.isMediaPlaying.value == true) {
Log.i("$TAG Paused, stopping media player")
viewModel.pause()
}
super.onPause()
}
override fun onDestroyView() {
binding.videoPlayer.stopPlayback()
super.onDestroyView()
}
private fun initVideoPlayer(path: String) {
Log.i("$TAG Creating video player for file [$path]")
binding.videoPlayer.setVideoPath(path)
binding.videoPlayer.setOnCompletionListener {
Log.i("$TAG End of file reached")
viewModel.isVideoPlaying.value = false
}
}
}

View file

@ -40,6 +40,7 @@ import org.linphone.core.tools.Log
import org.linphone.ui.GenericViewModel
import org.linphone.utils.Event
import org.linphone.utils.FileUtils
import org.linphone.utils.TimestampUtils
class FileViewModel @UiThread constructor() : GenericViewModel() {
companion object {
@ -64,6 +65,8 @@ class FileViewModel @UiThread constructor() : GenericViewModel() {
val fileReadyEvent = MutableLiveData<Event<Boolean>>()
val dateTime = MutableLiveData<String>()
val exportPlainTextFileEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
@ -98,12 +101,18 @@ class FileViewModel @UiThread constructor() : GenericViewModel() {
}
@UiThread
fun loadFile(file: String, content: String? = null) {
fun loadFile(file: String, timestamp: Long, content: String? = null) {
fullScreenMode.value = true
val name = FileUtils.getNameFromFilePath(file)
fileName.value = name
dateTime.value = TimestampUtils.toString(
timestamp,
shortDate = false,
hideYear = false
)
if (!content.isNullOrEmpty()) {
isText.value = true
text.postValue(content!!)

View file

@ -39,6 +39,8 @@ class MediaListViewModel @UiThread constructor() : AbstractConversationViewModel
val currentlyDisplayedFileName = MutableLiveData<String>()
val currentlyDisplayedFileDateTime = MutableLiveData<String>()
override fun beforeNotifyingChatRoomFound(sameOne: Boolean) {
loadMediaList()
}

View file

@ -23,10 +23,16 @@ import android.media.AudioAttributes
import android.media.MediaPlayer
import androidx.annotation.UiThread
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.ticker
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.linphone.core.tools.Log
import org.linphone.ui.GenericViewModel
import org.linphone.utils.Event
import org.linphone.utils.FileUtils
import org.linphone.utils.TimestampUtils
class MediaViewModel @UiThread constructor() : GenericViewModel() {
companion object {
@ -43,24 +49,29 @@ class MediaViewModel @UiThread constructor() : GenericViewModel() {
val isVideo = MutableLiveData<Boolean>()
val isVideoPlaying = MutableLiveData<Boolean>()
val isAudio = MutableLiveData<Boolean>()
val isAudioPlaying = MutableLiveData<Boolean>()
val isMediaPlaying = MutableLiveData<Boolean>()
val toggleVideoPlayPauseEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
val duration = MutableLiveData<Int>()
val formattedDuration = MutableLiveData<String>()
val position = MutableLiveData<Int>()
lateinit var mediaPlayer: MediaPlayer
private lateinit var filePath: String
private lateinit var mediaPlayer: MediaPlayer
private val tickerChannel = ticker(1000, 1000)
private var updatePositionJob: Job? = null
override fun onCleared() {
if (::mediaPlayer.isInitialized) {
mediaPlayer.release()
}
stopUpdatePlaybackPosition()
super.onCleared()
}
@ -81,14 +92,13 @@ class MediaViewModel @UiThread constructor() : GenericViewModel() {
}
FileUtils.MimeType.Video -> {
Log.d("$TAG File [$file] seems to be a video")
initMediaPlayer()
isVideo.value = true
isVideoPlaying.value = false
}
FileUtils.MimeType.Audio -> {
Log.d("$TAG File [$file] seems to be an audio file")
isAudio.value = true
initMediaPlayer()
isAudio.value = true
}
else -> {
Log.e("$TAG Unexpected MIME type [$mime] for file at [$file]")
@ -102,35 +112,44 @@ class MediaViewModel @UiThread constructor() : GenericViewModel() {
}
@UiThread
fun playPauseVideo() {
val playVideo = isVideoPlaying.value == false
isVideoPlaying.value = playVideo
toggleVideoPlayPauseEvent.value = Event(playVideo)
}
@UiThread
fun playPauseAudio() {
fun togglePlayPause() {
if (::mediaPlayer.isInitialized) {
if (mediaPlayer.isPlaying) {
mediaPlayer.pause()
isAudioPlaying.value = false
isMediaPlaying.value = false
stopUpdatePlaybackPosition()
} else {
mediaPlayer.start()
isAudioPlaying.value = true
isMediaPlaying.value = true
startUpdatePlaybackPosition()
}
}
}
@UiThread
fun pauseAudio() {
fun play() {
if (::mediaPlayer.isInitialized) {
mediaPlayer.start()
startUpdatePlaybackPosition()
isMediaPlaying.value = true
}
}
@UiThread
fun pause() {
if (::mediaPlayer.isInitialized) {
isMediaPlaying.value = false
stopUpdatePlaybackPosition()
mediaPlayer.pause()
}
}
@UiThread
private fun initMediaPlayer() {
isAudioPlaying.value = false
isMediaPlaying.value = false
mediaPlayer = MediaPlayer().apply {
setAudioAttributes(
AudioAttributes.Builder().setContentType(AudioAttributes.CONTENT_TYPE_MUSIC).setUsage(
@ -140,12 +159,39 @@ class MediaViewModel @UiThread constructor() : GenericViewModel() {
setDataSource(filePath)
setOnCompletionListener {
Log.i("$TAG Media player reached the end of file")
isAudioPlaying.postValue(false)
isMediaPlaying.postValue(false)
position.postValue(0)
stopUpdatePlaybackPosition()
// Leave full screen when playback is done
fullScreenMode.postValue(false)
}
prepare()
start()
isAudioPlaying.value = true
}
Log.i("$TAG Media player for file [$filePath] created")
val durationInMillis = mediaPlayer.duration
position.value = 0
duration.value = durationInMillis
formattedDuration.value = TimestampUtils.durationToString(durationInMillis / 1000)
Log.i("$TAG Media player for file [$filePath] created, let's start it")
}
@UiThread
fun startUpdatePlaybackPosition() {
updatePositionJob = viewModelScope.launch {
withContext(Dispatchers.IO) {
for (tick in tickerChannel) {
if (::mediaPlayer.isInitialized && mediaPlayer.isPlaying) {
position.postValue(mediaPlayer.currentPosition)
}
}
}
}
}
@UiThread
fun stopUpdatePlaybackPosition() {
updatePositionJob?.cancel()
updatePositionJob = null
}
}

View file

@ -132,6 +132,7 @@ class FileModel @AnyThread constructor(
FileUtils.deleteFile(path)
}
@AnyThread
private fun getDuration() {
try {
val retriever = MediaMetadataRetriever()

View file

@ -122,6 +122,8 @@ class DebugFragment : GenericMainFragment() {
val bundle = Bundle()
bundle.putString("path", CorePreferences.CONFIG_FILE_NAME)
bundle.putString("content", content)
val nowInSeconds = System.currentTimeMillis() / 1000
bundle.putLong("timestamp", nowInSeconds)
intent.putExtras(bundle)
startActivity(intent)
}

View file

@ -25,6 +25,7 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import androidx.media.AudioFocusRequestCompat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.channels.ticker
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@ -67,6 +68,7 @@ class RecordingsListViewModel @UiThread constructor() : GenericViewModel() {
}
private val tickerChannel = ticker(1000, 1000)
private var updatePositionJob: Job? = null
init {
searchBarVisible.value = false
@ -166,7 +168,7 @@ class RecordingsListViewModel @UiThread constructor() : GenericViewModel() {
player?.start()
model.isPlaying.postValue(true)
viewModelScope.launch {
updatePositionJob = viewModelScope.launch {
withContext(Dispatchers.IO) {
for (tick in tickerChannel) {
coreContext.postOnCoreThread {
@ -193,6 +195,8 @@ class RecordingsListViewModel @UiThread constructor() : GenericViewModel() {
player?.pause()
model.isPlaying.postValue(false)
updatePositionJob?.cancel()
updatePositionJob = null
}
@WorkerThread

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="256"
android:viewportHeight="256">
<path
android:pathData="M212.92,17.69a8,8 0,0 0,-6.86 -1.45l-128,32A8,8 0,0 0,72 56V166.08A36,36 0,1 0,88 196V110.25l112,-28v51.83A36,36 0,1 0,216 164V24A8,8 0,0 0,212.92 17.69ZM52,216a20,20 0,1 1,20 -20A20,20 0,0 1,52 216ZM88,93.75V62.25l112,-28v31.5ZM180,184a20,20 0,1 1,20 -20A20,20 0,0 1,180 184Z"
android:fillColor="#4e6074"/>
</vector>

View file

@ -27,7 +27,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
app:constraint_referenced_ids="back, title, share, save, file_name"/>
app:constraint_referenced_ids="top_bar_background, back, file_name, share, save, date_time"/>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/top_bar_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="back, date_time"
app:barrierDirection="bottom" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/media_view_pager"
@ -35,78 +42,85 @@
android:layout_height="match_parent"
android:orientation="horizontal" />
<View
android:id="@+id/top_bar_background"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="@id/top_bar_barrier"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/back"
android:onClick="@{backClickListener}"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="?attr/color_main2_000"
android:layout_height="@dimen/top_bar_height"
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/caret_left"
android:contentDescription="@string/content_description_go_back_icon"
app:tint="?attr/color_main1_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/main_page_title_style"
android:id="@+id/title"
style="@style/default_text_style_700"
android:id="@+id/file_name"
android:layout_width="0dp"
android:layout_height="@dimen/top_bar_height"
android:background="?attr/color_main2_000"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:layout_height="wrap_content"
android:text="@{viewModel.currentlyDisplayedFileName, default=`nomdufichier.jpg`}"
android:textSize="13sp"
android:textColor="?attr/color_main2_600"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintEnd_toStartOf="@id/share"
app:layout_constraintStart_toEndOf="@id/back"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/date_time"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/date_time"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@{viewModel.currentlyDisplayedFileDateTime, default=`envoyé le 02/05/2023 à 11h05`}"
android:textSize="12sp"
android:textColor="?attr/color_main2_500"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintTop_toBottomOf="@id/file_name"
app:layout_constraintBottom_toBottomOf="@id/back"
app:layout_constraintEnd_toStartOf="@id/share"
app:layout_constraintStart_toEndOf="@id/back"/>
<ImageView
android:id="@+id/share"
android:onClick="@{shareClickListener}"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="?attr/color_main2_000"
android:layout_height="@dimen/top_bar_height"
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/share_network"
android:contentDescription="@string/content_description_share_file"
app:tint="?attr/color_main1_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toStartOf="@id/save"
app:layout_constraintTop_toTopOf="@id/title" />
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/save"
android:onClick="@{exportClickListener}"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="?attr/color_main2_000"
android:layout_height="@dimen/top_bar_height"
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/download_simple"
android:contentDescription="@string/content_description_save_file"
app:tint="?attr/color_main1_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/file_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?attr/color_main2_000"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:text="@{viewModel.currentlyDisplayedFileName, default=`nomdufichier.jpg\nenvoyé le 02/05/2023 à 11h05`}"
android:textSize="12sp"
android:textColor="?attr/color_main2_600"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/toasts_area"

View file

@ -19,22 +19,13 @@
android:layout_height="match_parent"
android:background="@color/black">
<ImageView
android:id="@+id/play_pause_audio_playback"
android:onClick="@{() -> viewModel.playPauseAudio()}"
<androidx.constraintlayout.widget.Group
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:contentDescription="@string/content_description_play_pause_video_playback"
android:src="@{viewModel.isAudioPlaying ? @drawable/pause_fill : @drawable/play_fill, default=@drawable/play_fill}"
android:visibility="@{viewModel.isAudio ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:tint="@color/white"/>
android:visibility="@{viewModel.fullScreenMode ? View.GONE : viewModel.isAudio || viewModel.isVideo ? View.VISIBLE : View.GONE}"
app:constraint_referenced_ids="play_pause_audio_playback, progress, duration" />
<VideoView
<TextureView
android:id="@+id/video_player"
android:layout_width="0dp"
android:layout_height="wrap_content"
@ -45,31 +36,72 @@
app:layout_constraintEnd_toEndOf="parent"/>
<ImageView
android:id="@+id/play_pause_video_playback"
android:onClick="@{() -> viewModel.playPauseVideo()}"
android:id="@+id/audio_file_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:contentDescription="@string/content_description_play_pause_video_playback"
android:src="@{viewModel.isVideoPlaying ? @drawable/pause_fill : @drawable/play_fill, default=@drawable/play_fill}"
android:visibility="@{viewModel.isVideo &amp;&amp; !viewModel.fullScreenMode ? View.VISIBLE : View.GONE, default=gone}"
android:src="@drawable/music_notes"
android:contentDescription="@null"
android:visibility="@{viewModel.isAudio ? View.VISIBLE : View.GONE, default=gone}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:tint="@color/white"/>
app:tint="@color/white" />
<com.github.chrisbanes.photoview.PhotoView
android:id="@+id/image"
android:onClick="@{toggleFullScreenModeClickListener}"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/illu"
android:adjustViewBounds="true"
android:scaleType="fitCenter"
coilFile="@{viewModel.path}"
android:visibility="@{viewModel.isImage ? View.VISIBLE : View.GONE, default=gone}" />
<ImageView
android:id="@+id/play_pause_audio_playback"
android:onClick="@{() -> viewModel.togglePlayPause()}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginBottom="@dimen/screen_bottom_margin"
android:padding="8dp"
android:contentDescription="@string/content_description_play_pause_video_playback"
android:src="@{viewModel.isMediaPlaying ? @drawable/pause_fill : @drawable/play_fill, default=@drawable/play_fill}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:tint="@color/white"/>
<com.google.android.material.progressindicator.LinearProgressIndicator
android:id="@+id/progress"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:max="@{viewModel.duration, default=100}"
android:progress="@{viewModel.position, default=75}"
app:trackCornerRadius="5dp"
app:trackThickness="10dp"
app:trackColor="?attr/color_main1_100"
app:indicatorColor="?attr/color_main1_500"
app:layout_constraintTop_toTopOf="@id/play_pause_audio_playback"
app:layout_constraintBottom_toBottomOf="@id/play_pause_audio_playback"
app:layout_constraintStart_toEndOf="@id/play_pause_audio_playback"
app:layout_constraintEnd_toStartOf="@id/duration"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_700"
android:id="@+id/duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:text="@{viewModel.formattedDuration, default=`00:42`}"
android:textSize="13sp"
android:textColor="@color/white"
app:layout_constraintTop_toTopOf="@id/play_pause_audio_playback"
app:layout_constraintBottom_toBottomOf="@id/play_pause_audio_playback"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View file

@ -25,7 +25,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="@{viewModel.fullScreenMode ? View.GONE : View.VISIBLE}"
app:constraint_referenced_ids="back, title, share, save, file_name"/>
app:constraint_referenced_ids="top_bar_background, back, file_name, share, save, date_time"/>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/top_bar_barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="back, date_time"
app:barrierDirection="bottom" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pdf_view_pager"
@ -48,7 +55,7 @@
android:text="@{viewModel.pdfCurrentPage + ` / ` + viewModel.pdfPages, default=`1 / 30`}"
android:textColor="@color/white"
android:background="#80000000"
app:layout_constraintBottom_toTopOf="@id/file_name"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="@id/pdf_view_pager"
app:layout_constraintEnd_toEndOf="@id/pdf_view_pager" />
@ -75,79 +82,85 @@
</ScrollView>
<View
android:id="@+id/top_bar_background"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@color/white"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="@id/top_bar_barrier"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<ImageView
android:id="@+id/back"
android:onClick="@{backClickListener}"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="?attr/color_main2_000"
android:layout_height="@dimen/top_bar_height"
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/caret_left"
android:contentDescription="@string/content_description_go_back_icon"
app:tint="?attr/color_main1_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/main_page_title_style"
android:id="@+id/title"
style="@style/default_text_style_700"
android:id="@+id/file_name"
android:layout_width="0dp"
android:layout_height="@dimen/top_bar_height"
android:background="?attr/color_main2_000"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:text=""
android:layout_height="wrap_content"
android:text="@{viewModel.fileName, default=`nomdufichier.jpg`}"
android:textSize="13sp"
android:textColor="?attr/color_main2_600"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintEnd_toStartOf="@id/share"
app:layout_constraintStart_toEndOf="@id/back"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/date_time"/>
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/date_time"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@{viewModel.dateTime, default=`envoyé le 02/05/2023 à 11h05`}"
android:textSize="12sp"
android:textColor="?attr/color_main2_500"
android:maxLines="1"
android:ellipsize="end"
app:layout_constraintTop_toBottomOf="@id/file_name"
app:layout_constraintBottom_toBottomOf="@id/back"
app:layout_constraintEnd_toStartOf="@id/share"
app:layout_constraintStart_toEndOf="@id/back"/>
<ImageView
android:id="@+id/share"
android:onClick="@{shareClickListener}"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="?attr/color_main2_000"
android:layout_height="@dimen/top_bar_height"
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/share_network"
android:contentDescription="@string/content_description_share_file"
app:tint="?attr/color_main1_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toStartOf="@id/save"
app:layout_constraintTop_toTopOf="@id/title" />
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/save"
android:onClick="@{() -> viewModel.exportToMediaStore()}"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:background="?attr/color_main2_000"
android:layout_height="@dimen/top_bar_height"
android:adjustViewBounds="true"
android:padding="15dp"
android:src="@drawable/download_simple"
android:contentDescription="@string/content_description_save_file"
app:tint="?attr/color_main1_500"
app:layout_constraintBottom_toBottomOf="@id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/title" />
<androidx.appcompat.widget.AppCompatTextView
style="@style/default_text_style_300"
android:id="@+id/file_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="?attr/color_main2_000"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:text="@{viewModel.fileName, default=`nomdufichier.jpg\nenvoyé le 02/05/2023 à 11h05`}"
android:textSize="12sp"
android:textColor="?attr/color_main2_600"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"/>
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout
android:id="@+id/toasts_area"