Added PDF file viewer

This commit is contained in:
Sylvain Berfini 2023-11-16 16:18:08 +01:00
parent 73229f51a1
commit b6c146f123
6 changed files with 203 additions and 4 deletions

View file

@ -285,8 +285,8 @@ class ConversationFragment : GenericFragment() {
if (repliedMessageId.isNullOrEmpty()) {
Log.w("$TAG Chat message [${model.id}] doesn't have a reply to ID!")
} else {
val originalMessage = adapter.currentList.find {
!it.isEvent && (it.model as ChatMessageModel).id == repliedMessageId
val originalMessage = adapter.currentList.find { eventLog ->
!eventLog.isEvent && (eventLog.model as ChatMessageModel).id == repliedMessageId
}
if (originalMessage != null) {
val position = adapter.currentList.indexOf(originalMessage)

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2010-2021 Belledonne Communications SARL.
*
* This file is part of linphone-android
* (see https://www.linphone.org).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.linphone.ui.main.viewer.adapter
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import org.linphone.R
import org.linphone.ui.main.viewer.viewmodel.FileViewModel
class PdfPagesListAdapter(private val viewModel: FileViewModel) : RecyclerView.Adapter<PdfPagesListAdapter.PdfPageViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PdfPageViewHolder {
val view = LayoutInflater.from(parent.context).inflate(
R.layout.file_pdf_viewer_page,
parent,
false
)
return PdfPageViewHolder(view)
}
override fun getItemCount(): Int {
return viewModel.getPagesCount()
}
override fun onBindViewHolder(holder: PdfPageViewHolder, position: Int) {
holder.bind(position)
}
inner class PdfPageViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
fun bind(index: Int) {
viewModel.loadPdfPageInto(index, view.findViewById(R.id.pdf_image))
}
}
}

View file

@ -1,6 +1,7 @@
package org.linphone.ui.main.viewer.fragment
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -11,6 +12,7 @@ import androidx.navigation.fragment.navArgs
import org.linphone.core.tools.Log
import org.linphone.databinding.FileImageViewerFragmentBinding
import org.linphone.ui.main.fragment.GenericFragment
import org.linphone.ui.main.viewer.adapter.PdfPagesListAdapter
import org.linphone.ui.main.viewer.viewmodel.FileViewModel
@UiThread
@ -23,6 +25,8 @@ class FileViewerFragment : GenericFragment() {
private lateinit var viewModel: FileViewModel
private lateinit var adapter: PdfPagesListAdapter
private val args: FileViewerFragmentArgs by navArgs()
override fun onCreateView(
@ -54,5 +58,19 @@ class FileViewerFragment : GenericFragment() {
binding.setBackClickListener {
goBack()
}
viewModel.pdfRendererReadyEvent.observe(viewLifecycleOwner) {
it.consume {
Log.i("$TAG PDF renderer is ready, attaching adapter to ViewPager")
val displayMetrics = DisplayMetrics()
requireActivity().windowManager.defaultDisplay.getMetrics(displayMetrics)
viewModel.screenHeight = displayMetrics.heightPixels
viewModel.screenWidth = displayMetrics.widthPixels
adapter = PdfPagesListAdapter(viewModel)
binding.pdfViewPager.adapter = adapter
binding.dotsIndicator.attachTo(binding.pdfViewPager)
}
}
}
}

View file

@ -1,23 +1,118 @@
package org.linphone.ui.main.viewer.viewmodel
import android.graphics.Bitmap
import android.graphics.pdf.PdfRenderer
import android.os.ParcelFileDescriptor
import android.widget.ImageView
import androidx.annotation.UiThread
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import java.io.File
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.linphone.core.tools.Log
import org.linphone.utils.Event
import org.linphone.utils.FileUtils
class FileViewModel @UiThread constructor() : ViewModel() {
companion object {
private const val TAG = "[File ViewModel]"
}
val path = MutableLiveData<String>()
val fileName = MutableLiveData<String>()
val fullScreenMode = MutableLiveData<Boolean>()
val isPdf = MutableLiveData<Boolean>()
val pdfRendererReadyEvent: MutableLiveData<Event<Boolean>> by lazy {
MutableLiveData<Event<Boolean>>()
}
// Below are required for PDF viewer
private lateinit var pdfRenderer: PdfRenderer
var screenWidth: Int = 0
var screenHeight: Int = 0
// End of PDF viewer required variables
init {
fullScreenMode.value = true
}
@UiThread
fun loadFile(file: String) {
path.postValue(file)
val name = FileUtils.getNameFromFilePath(file)
fileName.value = name
val extension = FileUtils.getExtensionFromFileName(name)
if (extension == "pdf") {
Log.i("$TAG File [$file] seems to be a PDF")
isPdf.value = true
fullScreenMode.value = false
viewModelScope.launch {
withContext(Dispatchers.IO) {
val input = ParcelFileDescriptor.open(
File(file),
ParcelFileDescriptor.MODE_READ_ONLY
)
pdfRenderer = PdfRenderer(input)
Log.i("$TAG ${pdfRenderer.pageCount} pages in file $file")
pdfRendererReadyEvent.postValue(Event(true))
}
}
} else {
path.value = file
}
}
override fun onCleared() {
if (::pdfRenderer.isInitialized) {
pdfRenderer.close()
}
super.onCleared()
}
@UiThread
fun toggleFullScreen() {
fullScreenMode.value = fullScreenMode.value != true
}
@UiThread
fun getPagesCount(): Int {
if (::pdfRenderer.isInitialized) {
return pdfRenderer.pageCount
}
return 0
}
@UiThread
fun loadPdfPageInto(index: Int, view: ImageView) {
viewModelScope.launch {
withContext(Dispatchers.IO) {
try {
val page: PdfRenderer.Page = pdfRenderer.openPage(index)
val width = if (screenWidth <= screenHeight) screenWidth else screenHeight
val bm = Bitmap.createBitmap(
width,
(width / page.width * page.height),
Bitmap.Config.ARGB_8888
)
page.render(bm, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
page.close()
withContext(Dispatchers.Main) {
view.setImageBitmap(bm)
}
} catch (e: Exception) {
Log.e("$TAG Exception: $e")
}
}
}
}
}

View file

@ -33,11 +33,40 @@
android:adjustViewBounds="true"
android:scaleType="fitCenter"
coilFile="@{viewModel.path}"
android:visibility="@{viewModel.isPdf ? View.GONE : View.VISIBLE}"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pdf_view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:visibility="@{viewModel.isPdf ? 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" />
<com.tbuonomo.viewpagerdotsindicator.DotsIndicator
android:id="@+id/dots_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="20dp"
android:visibility="@{viewModel.isPdf ? View.VISIBLE : View.GONE, default=gone}"
app:dotsColor="@color/gray_main2_200"
app:dotsCornerRadius="8dp"
app:dotsSize="13dp"
app:dotsSpacing="5dp"
app:dotsWidthFactor="2.5"
app:selectedDotColor="@color/orange_main_500"
app:progressMode="false"
app:layout_constraintBottom_toTopOf="@id/file_name"
app:layout_constraintStart_toStartOf="@id/pdf_view_pager"
app:layout_constraintEnd_toEndOf="@id/pdf_view_pager" />
<ImageView
android:id="@+id/back"
android:onClick="@{backClickListener}"
@ -105,7 +134,7 @@
android:background="@color/white"
android:paddingTop="16dp"
android:paddingBottom="16dp"
android:text="nomdufichier.jpg\nenvoyé le 02/05/2023 à 11h05"
android:text="@{viewModel.fileName, default=`nomdufichier.jpg\nenvoyé le 02/05/2023 à 11h05`}"
android:textSize="12sp"
android:textColor="@color/gray_main2_400"
android:textAlignment="center"

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<com.github.chrisbanes.photoview.PhotoView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pdf_image"
android:layout_width="match_parent"
android:layout_height="match_parent" />