1 /* <lambda>null2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.pdfrendererbasic 18 19 import android.app.Application 20 import android.graphics.Bitmap 21 import android.graphics.pdf.PdfRenderer 22 import android.os.ParcelFileDescriptor 23 import androidx.lifecycle.AndroidViewModel 24 import androidx.lifecycle.LiveData 25 import androidx.lifecycle.MutableLiveData 26 import kotlinx.coroutines.CoroutineScope 27 import kotlinx.coroutines.Job 28 import kotlinx.coroutines.asCoroutineDispatcher 29 import kotlinx.coroutines.launch 30 import java.io.File 31 import java.util.concurrent.Executor 32 import java.util.concurrent.Executors 33 34 class PdfRendererBasicViewModel @JvmOverloads constructor( 35 application: Application, 36 useInstantExecutor: Boolean = false 37 ) : AndroidViewModel(application) { 38 39 companion object { 40 const val FILENAME = "sample.pdf" 41 } 42 43 private val job = Job() 44 private val executor = if (useInstantExecutor) { 45 Executor { it.run() } 46 } else { 47 Executors.newSingleThreadExecutor() 48 } 49 private val scope = CoroutineScope(executor.asCoroutineDispatcher() + job) 50 51 private var fileDescriptor: ParcelFileDescriptor? = null 52 private var pdfRenderer: PdfRenderer? = null 53 private var currentPage: PdfRenderer.Page? = null 54 private var cleared = false 55 56 private val _pageBitmap = MutableLiveData<Bitmap>() 57 val pageBitmap: LiveData<Bitmap> 58 get() = _pageBitmap 59 60 private val _previousEnabled = MutableLiveData<Boolean>() 61 val previousEnabled: LiveData<Boolean> 62 get() = _previousEnabled 63 64 private val _nextEnabled = MutableLiveData<Boolean>() 65 val nextEnabled: LiveData<Boolean> 66 get() = _nextEnabled 67 68 private val _pageInfo = MutableLiveData<Pair<Int, Int>>() 69 val pageInfo: LiveData<Pair<Int, Int>> 70 get() = _pageInfo 71 72 init { 73 scope.launch { 74 openPdfRenderer() 75 showPage(0) 76 if (cleared) { 77 closePdfRenderer() 78 } 79 } 80 } 81 82 override fun onCleared() { 83 super.onCleared() 84 scope.launch { 85 closePdfRenderer() 86 cleared = true 87 job.cancel() 88 } 89 } 90 91 fun showPrevious() { 92 scope.launch { 93 currentPage?.let { page -> 94 if (page.index > 0) { 95 showPage(page.index - 1) 96 } 97 } 98 } 99 } 100 101 fun showNext() { 102 scope.launch { 103 pdfRenderer?.let { renderer -> 104 currentPage?.let { page -> 105 if (page.index + 1 < renderer.pageCount) { 106 showPage(page.index + 1) 107 } 108 } 109 } 110 } 111 } 112 113 private fun openPdfRenderer() { 114 val application = getApplication<Application>() 115 val file = File(application.cacheDir, FILENAME) 116 if (!file.exists()) { 117 application.assets.open(FILENAME).use { asset -> 118 file.writeBytes(asset.readBytes()) 119 } 120 } 121 fileDescriptor = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY).also { 122 pdfRenderer = PdfRenderer(it) 123 } 124 } 125 126 private fun showPage(index: Int) { 127 // Make sure to close the current page before opening another one. 128 currentPage?.let { page -> 129 currentPage = null 130 page.close() 131 } 132 pdfRenderer?.let { renderer -> 133 // Use `openPage` to open a specific page in PDF. 134 val page = renderer.openPage(index).also { 135 currentPage = it 136 } 137 // Important: the destination bitmap must be ARGB (not RGB). 138 val bitmap = Bitmap.createBitmap(page.width, page.height, Bitmap.Config.ARGB_8888) 139 // Here, we render the page onto the Bitmap. 140 // To render a portion of the page, use the second and third parameter. Pass nulls to get 141 // the default result. 142 // Pass either RENDER_MODE_FOR_DISPLAY or RENDER_MODE_FOR_PRINT for the last parameter. 143 page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY) 144 _pageBitmap.postValue(bitmap) 145 val count = renderer.pageCount 146 _pageInfo.postValue(index to count) 147 _previousEnabled.postValue(index > 0) 148 _nextEnabled.postValue(index + 1 < count) 149 } 150 } 151 152 private fun closePdfRenderer() { 153 currentPage?.close() 154 pdfRenderer?.close() 155 fileDescriptor?.close() 156 } 157 158 } 159