From 0dafe1ff115a89f2c41e3e62c25e1d664e2127a3 Mon Sep 17 00:00:00 2001 From: Ekaterina Date: Wed, 23 Mar 2022 15:30:11 +0300 Subject: [PATCH 1/2] Internal-270 Feature base classes and view state for loading and rendering pdf files --- pdf-viewer/.gitignore | 1 + pdf-viewer/build.gradle | 40 ++++++++++++ pdf-viewer/src/main/AndroidManifest.xml | 2 + .../pdf_viewer/extensions/PdfRendereExt.kt | 24 +++++++ .../repository/PdfViewRepository.kt | 14 +++++ .../pdf_viewer/ui/base/BaseFragment.kt | 63 +++++++++++++++++++ .../pdf_viewer/viewmodel/BaseViewModel.kt | 46 ++++++++++++++ .../pdf_viewer/viewmodel/PdfViewModel.kt | 24 +++++++ .../viewstate/PdfReaderViewState.kt | 11 ++++ 9 files changed, 225 insertions(+) create mode 100644 pdf-viewer/.gitignore create mode 100644 pdf-viewer/build.gradle create mode 100644 pdf-viewer/src/main/AndroidManifest.xml create mode 100644 pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/extensions/PdfRendereExt.kt create mode 100644 pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/repository/PdfViewRepository.kt create mode 100644 pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/ui/base/BaseFragment.kt create mode 100644 pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/BaseViewModel.kt create mode 100644 pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/PdfViewModel.kt create mode 100644 pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewstate/PdfReaderViewState.kt diff --git a/pdf-viewer/.gitignore b/pdf-viewer/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/pdf-viewer/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/pdf-viewer/build.gradle b/pdf-viewer/build.gradle new file mode 100644 index 00000000..2690e7b0 --- /dev/null +++ b/pdf-viewer/build.gradle @@ -0,0 +1,40 @@ +plugins { + id 'com.android.library' + id 'org.jetbrains.kotlin.android' +} + +android { + compileSdk 31 + + defaultConfig { + minSdk 23 + targetSdk 31 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + + implementation 'androidx.core:core-ktx:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.1' + implementation 'com.google.android.material:material:1.5.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} \ No newline at end of file diff --git a/pdf-viewer/src/main/AndroidManifest.xml b/pdf-viewer/src/main/AndroidManifest.xml new file mode 100644 index 00000000..2a8167a3 --- /dev/null +++ b/pdf-viewer/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/extensions/PdfRendereExt.kt b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/extensions/PdfRendereExt.kt new file mode 100644 index 00000000..bae97ae6 --- /dev/null +++ b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/extensions/PdfRendereExt.kt @@ -0,0 +1,24 @@ +package ru.touchin.roboswag.pdf_reader.extensions + +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.pdf.PdfRenderer + +fun PdfRenderer.Page.renderBitmap(width: Int) = use { + val bitmap = createBitmap(width) + render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY) + bitmap +} + +private fun PdfRenderer.Page.createBitmap(bitmapWidth: Int): Bitmap { + val bitmap = Bitmap.createBitmap( + bitmapWidth, (bitmapWidth.toFloat() / width * height).toInt(), Bitmap.Config.ARGB_8888 + ) + + val canvas = Canvas(bitmap) + canvas.drawColor(Color.WHITE) + canvas.drawBitmap(bitmap, 0f, 0f, null) + + return bitmap +} \ No newline at end of file diff --git a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/repository/PdfViewRepository.kt b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/repository/PdfViewRepository.kt new file mode 100644 index 00000000..6aa2afdd --- /dev/null +++ b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/repository/PdfViewRepository.kt @@ -0,0 +1,14 @@ +package ru.touchin.roboswag.pdf_reader.repository + +import android.graphics.Bitmap +import java.io.File + +interface PdfViewRepository { + + suspend fun downloadPdf(fileUri: String, fileName: String) + + suspend fun getPdfFromStorage(fileUri: String) : File + + suspend fun renderSinglePage(filePath: String, width: Int) : Bitmap + +} \ No newline at end of file diff --git a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/ui/base/BaseFragment.kt b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/ui/base/BaseFragment.kt new file mode 100644 index 00000000..cfcec660 --- /dev/null +++ b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/ui/base/BaseFragment.kt @@ -0,0 +1,63 @@ +package ru.touchin.roboswag.pdf_reader.ui.base + +import android.graphics.Bitmap +import android.os.Bundle +import android.view.View +import android.widget.Toast +import androidx.fragment.app.Fragment +import ru.touchin.roboswag.pdf_reader.viewstate.PdfReaderViewState +import ru.touchin.roboswag.pdf_reader.BaseViewModel +import java.io.File + +abstract class BaseFragment : Fragment() { + + abstract val viewModel: VM + + protected open fun renderData(state: PdfReaderViewState) { + when (state) { + is PdfReaderViewState.ReadingSuccess -> downloadSuccess(state.data) + is PdfReaderViewState.RenderingSuccess -> renderSuccess(state.data) + is PdfReaderViewState.Error -> renderError(state.error) + is PdfReaderViewState.Loading -> setLoading(true) + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + viewModel.stateLiveData.observe(viewLifecycleOwner) { state -> + renderData(state) + } + + } + + protected open fun downloadSuccess(file: File) { + setLoading(false) + } + + protected open fun renderSuccess(bitmap: Bitmap) { + setLoading(false) + } + + protected open fun renderError(error: Throwable) { + setLoading(false) + error.message?.let { showMessage(it) } + } + + protected open fun renderMessage(message: String) { + setLoading(false) + showMessage(message) + } + + protected open fun setLoading(isLoading: Boolean) { + } + + private fun showMessage(message: String) { + Toast.makeText( + requireContext(), + message, + Toast.LENGTH_LONG + ).show() + } + +} \ No newline at end of file diff --git a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/BaseViewModel.kt b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/BaseViewModel.kt new file mode 100644 index 00000000..cbebc6f0 --- /dev/null +++ b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/BaseViewModel.kt @@ -0,0 +1,46 @@ +package ru.touchin.roboswag.pdf_reader.viewmodel + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import ru.touchin.roboswag.pdf_reader.viewstate.PdfReaderViewState +import kotlinx.coroutines.CoroutineExceptionHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.async +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.launch + +abstract class BaseViewModel( +) : ViewModel() { + + protected val mStateLiveData = MutableLiveData() + val stateLiveData get() = mStateLiveData as LiveData + + private val viewModelCoroutineScope = CoroutineScope( + Dispatchers.IO + + SupervisorJob() + + CoroutineExceptionHandler { _, throwable -> + handleError(throwable) + }) + + override fun onCleared() { + super.onCleared() + cancelJob() + } + + protected open fun cancelJob() { + viewModelCoroutineScope.coroutineContext.cancelChildren() + } + + private fun handleError(error: Throwable) { + mStateLiveData.postValue(PdfReaderViewState.Error(error)) + } + + protected fun runAsync(block: suspend () -> Unit) = + viewModelCoroutineScope.launch { + block() + } + +} \ No newline at end of file diff --git a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/PdfViewModel.kt b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/PdfViewModel.kt new file mode 100644 index 00000000..1d2f0a46 --- /dev/null +++ b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/PdfViewModel.kt @@ -0,0 +1,24 @@ +package ru.touchin.roboswag.pdf_reader.viewmodel + +import ru.touchin.roboswag.pdf_reader.viewstate.PdfReaderViewState +import ru.touchin.roboswag.pdf_reader.repository.PdfViewRepository + +class PdfViewModel(private val repository: PdfViewRepository) : BaseViewModel() { + + fun renderPage(filePath: String, width: Int) { + mStateLiveData.postValue(PdfReaderViewState.Loading(true)) + runAsync { + val bitmap = repository.renderSinglePage(filePath, width) + mStateLiveData.postValue(PdfReaderViewState.RenderingSuccess(bitmap)) + } + } + + fun downloadPdfFile(fileUri: String) { + mStateLiveData.postValue(PdfReaderViewState.Loading(true)) + runAsync { + val file = repository.getPdfFromStorage(fileUri) + mStateLiveData.postValue(PdfReaderViewState.ReadingSuccess(file)) + } + } + +} \ No newline at end of file diff --git a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewstate/PdfReaderViewState.kt b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewstate/PdfReaderViewState.kt new file mode 100644 index 00000000..c4257236 --- /dev/null +++ b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewstate/PdfReaderViewState.kt @@ -0,0 +1,11 @@ +package ru.touchin.roboswag.pdf_reader.viewstate + +import android.graphics.Bitmap +import java.io.File + +sealed class PdfReaderViewState { + data class ReadingSuccess(val data: File) : PdfReaderViewState() + data class RenderingSuccess(val data: Bitmap) : PdfReaderViewState() + data class Error(val error: Throwable) : PdfReaderViewState() + data class Loading(val isLoading: Boolean) : PdfReaderViewState() +} \ No newline at end of file From 31249a5f6d553cd2ca6fedbae647331ee4572cd4 Mon Sep 17 00:00:00 2001 From: Ekaterina Date: Fri, 25 Mar 2022 18:26:23 +0300 Subject: [PATCH 2/2] Internal-270 Fix removing functions for loading file --- pdf-viewer/consumer-rules.pro | 0 .../roboswag/pdf_viewer/repository/PdfViewRepository.kt | 4 ---- .../touchin/roboswag/pdf_viewer/ui/base/BaseFragment.kt | 5 ----- .../touchin/roboswag/pdf_viewer/viewmodel/PdfViewModel.kt | 8 -------- .../roboswag/pdf_viewer/viewstate/PdfReaderViewState.kt | 1 - 5 files changed, 18 deletions(-) create mode 100644 pdf-viewer/consumer-rules.pro diff --git a/pdf-viewer/consumer-rules.pro b/pdf-viewer/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/repository/PdfViewRepository.kt b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/repository/PdfViewRepository.kt index 6aa2afdd..b1515cfc 100644 --- a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/repository/PdfViewRepository.kt +++ b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/repository/PdfViewRepository.kt @@ -5,10 +5,6 @@ import java.io.File interface PdfViewRepository { - suspend fun downloadPdf(fileUri: String, fileName: String) - - suspend fun getPdfFromStorage(fileUri: String) : File - suspend fun renderSinglePage(filePath: String, width: Int) : Bitmap } \ No newline at end of file diff --git a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/ui/base/BaseFragment.kt b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/ui/base/BaseFragment.kt index cfcec660..971dd95b 100644 --- a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/ui/base/BaseFragment.kt +++ b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/ui/base/BaseFragment.kt @@ -15,7 +15,6 @@ abstract class BaseFragment : Fragment() { protected open fun renderData(state: PdfReaderViewState) { when (state) { - is PdfReaderViewState.ReadingSuccess -> downloadSuccess(state.data) is PdfReaderViewState.RenderingSuccess -> renderSuccess(state.data) is PdfReaderViewState.Error -> renderError(state.error) is PdfReaderViewState.Loading -> setLoading(true) @@ -31,10 +30,6 @@ abstract class BaseFragment : Fragment() { } - protected open fun downloadSuccess(file: File) { - setLoading(false) - } - protected open fun renderSuccess(bitmap: Bitmap) { setLoading(false) } diff --git a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/PdfViewModel.kt b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/PdfViewModel.kt index 1d2f0a46..19cba5b1 100644 --- a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/PdfViewModel.kt +++ b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewmodel/PdfViewModel.kt @@ -13,12 +13,4 @@ class PdfViewModel(private val repository: PdfViewRepository) : BaseViewModel() } } - fun downloadPdfFile(fileUri: String) { - mStateLiveData.postValue(PdfReaderViewState.Loading(true)) - runAsync { - val file = repository.getPdfFromStorage(fileUri) - mStateLiveData.postValue(PdfReaderViewState.ReadingSuccess(file)) - } - } - } \ No newline at end of file diff --git a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewstate/PdfReaderViewState.kt b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewstate/PdfReaderViewState.kt index c4257236..31b2a471 100644 --- a/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewstate/PdfReaderViewState.kt +++ b/pdf-viewer/src/main/java/ru/touchin/roboswag/pdf_viewer/viewstate/PdfReaderViewState.kt @@ -4,7 +4,6 @@ import android.graphics.Bitmap import java.io.File sealed class PdfReaderViewState { - data class ReadingSuccess(val data: File) : PdfReaderViewState() data class RenderingSuccess(val data: Bitmap) : PdfReaderViewState() data class Error(val error: Throwable) : PdfReaderViewState() data class Loading(val isLoading: Boolean) : PdfReaderViewState()