Dynamically create Presentations when displays are added/removed

This commit is contained in:
Luke Hubmayer-Werner 2024-08-19 17:36:58 +09:30
parent f9b7db0e44
commit 96404488f1
2 changed files with 60 additions and 49 deletions

View File

@ -5,9 +5,9 @@ import android.content.Intent
import android.graphics.PorterDuff
import android.hardware.display.DisplayManager
import android.hardware.display.DisplayManager.DisplayListener
import android.media.MediaRouter
import android.os.Bundle
import android.util.DisplayMetrics
import android.view.Display
import android.view.Menu
import android.view.WindowManager
import android.widget.ImageView
@ -33,12 +33,11 @@ class MainActivity : AppCompatActivity() {
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding
private lateinit var displayManager: DisplayManager
private lateinit var mediaRouter: MediaRouter
private val thumbnailWidth = 500
private val thumbnailHeight = 700
private var renderAutoCrop = true
private var pagesPerLandscape = 3F
private var presentation: MyPresentation? = null
private val presentations = mutableMapOf<Int, MyPresentation>()
private lateinit var pdfDocument: PdfDocument
private val showPages = mutableListOf<Boolean>()
private val thumbnailImageViews = mutableListOf<ImageView>()
@ -46,18 +45,27 @@ class MainActivity : AppCompatActivity() {
private val displayListener = object: DisplayListener {
override fun onDisplayAdded(p0: Int) {
updateDisplayText()
reevaluateDisplay(p0)
}
override fun onDisplayChanged(p0: Int) {
updateDisplayText()
reevaluateDisplay(p0)
}
override fun onDisplayRemoved(p0: Int) {
updateDisplayText()
reevaluateDisplay(p0)
}
}
private fun reevaluateDisplay(p0: Int) {
presentations.remove(p0)?.cancel() // Pop any presentation associated with this display index, and clean it up
displayManager.getDisplay(p0)?.let { display ->
if (display.flags and Display.FLAG_PRESENTATION == 0) return
makePresentationView(display)
}
updateDisplayText()
}
private fun inputStreamToCache(outputFilename: String, inputStream: InputStream): File {
val fileCached = File(cacheDir, outputFilename)
val output = FileOutputStream(fileCached)
@ -69,30 +77,34 @@ class MainActivity : AppCompatActivity() {
return fileCached
}
private fun updatePresentationImages(reRender: Boolean = true) {
presentation?.let { p ->
if (reRender) {
println(p.displayMetrics)
val maxHeight: Int = p.displayMetrics.heightPixels
val maxWidth: Int = min(maxHeight * 5 / 7, (p.displayMetrics.widthPixels / pagesPerLandscape).toInt())
println("Rendering pages with maxHeight=$maxHeight maxWidth=$maxWidth")
pdfDocument.renderPagesPresentation(maxWidth, maxHeight, renderAutoCrop)
}
val bitmaps = showPages.withIndex()
.mapNotNull { (i, show) -> if (show) pdfDocument.bitmapPagesPresentation[i] else null }
.toList()
p.updateImages(bitmaps)
}
private fun updateBitmaps(maxWidth: Int, maxHeight: Int) {
println("Rendering pages with maxHeight=$maxHeight maxWidth=$maxWidth")
pdfDocument.renderPages(maxWidth, maxHeight, renderAutoCrop)
}
private fun makePresentationView() {
presentation = null
mediaRouter.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_VIDEO)?.presentationDisplay?.let { display ->
presentation = MyPresentation(this, display)
updatePresentationImages()
presentation?.setOnCancelListener { presentation = null }
presentation?.show()
}
private fun updatePresentationImages(p: MyPresentation) {
val maxHeight: Int = p.displayMetrics.heightPixels
val maxWidth: Int = min(maxHeight * 5 / 7, (p.displayMetrics.widthPixels / pagesPerLandscape).toInt())
updateBitmaps(maxWidth, maxHeight)
val key = Pair(maxWidth, maxHeight)
val bitmaps = showPages.withIndex()
.mapNotNull { (i, show) -> if (show) pdfDocument.bitmapPages[key]?.get(i) else null }
.toList()
p.updateImages(bitmaps)
}
private fun updatePresentations(reRender: Boolean = true) {
if (reRender) pdfDocument.invalidateCache()
presentations.values.forEach(::updatePresentationImages)
}
private fun makePresentationView(display: Display) {
val id = display.displayId
val presentation = MyPresentation(this, display)
updatePresentationImages(presentation)
//presentation.setOnCancelListener { presentations.remove(id) }
presentations[id] = presentation
presentation.show()
}
private fun reLayoutThumbnails() {
@ -103,11 +115,10 @@ class MainActivity : AppCompatActivity() {
showPages.map { if (it) container else disabledContainer }.zip(thumbnailImageViews).forEach { (cont, img) ->
cont.addView(img)
}
updatePresentationImages(false)
updatePresentations(false)
}
private fun populateThumbnails() {
val container = binding.appBarMain.contentMain.thumbnailsLayout
for ((index, bitmap) in pdfDocument.bitmapThumbnails) {
val img = ImageView(this)
img.setImageBitmap(bitmap)
@ -125,7 +136,7 @@ class MainActivity : AppCompatActivity() {
private fun updateDisplayText() {
val displayText = binding.appBarMain.contentMain.textDisplays
val text = StringBuilder()
//displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION).forEach {
displayManager.displays.forEach {
val displayMetrics = DisplayMetrics()
@Suppress("DEPRECATION")
@ -135,6 +146,7 @@ class MainActivity : AppCompatActivity() {
text.append(s)
text.append("\n")
}
text.append(presentations)
displayText.text = text.toString()
}
@ -142,14 +154,12 @@ class MainActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
mediaRouter = getSystemService(Context.MEDIA_ROUTER_SERVICE) as MediaRouter
//val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
//windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()) //Doesn't seem to actually fill the vacant space by default
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
updateDisplayText()
displayManager.registerDisplayListener(displayListener, null)
setSupportActionBar(binding.appBarMain.toolbar)
@ -164,13 +174,14 @@ class MainActivity : AppCompatActivity() {
handleIntent(intent)
pdfDocument.renderThumbnails(thumbnailWidth, thumbnailHeight)
makePresentationView()
displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION).forEach(::makePresentationView)
populateThumbnails()
val container = binding.appBarMain.contentMain.thumbnailsLayout
val containerScroll = binding.appBarMain.contentMain.thumbnailsScroll
containerScroll.viewTreeObserver.addOnScrollChangedListener {
presentation?.setScrollProgress(containerScroll.scrollX.toFloat() / (container.width - containerScroll.width))
val progress = containerScroll.scrollX.toFloat() / (container.width - containerScroll.width)
presentations.values.forEach {it.setScrollProgress(progress)}
}
binding.appBarMain.crop.setOnClickListener { view ->
@ -179,21 +190,21 @@ class MainActivity : AppCompatActivity() {
Snackbar.make(view, "Toggling Auto Crop $s", Snackbar.LENGTH_LONG)
.setAction("Action", null)
.setAnchorView(R.id.crop).show()
updatePresentationImages()
updatePresentations()
}
binding.appBarMain.zoomIn.setOnClickListener { view ->
pagesPerLandscape = max(pagesPerLandscape - 0.5F, 1F)
Snackbar.make(view, "Aiming for $pagesPerLandscape pages at a time", Snackbar.LENGTH_LONG)
.setAction("Action", null)
.setAnchorView(R.id.zoom_in).show()
updatePresentationImages()
updatePresentations()
}
binding.appBarMain.zoomOut.setOnClickListener { view ->
pagesPerLandscape = min(pagesPerLandscape + 0.5F, 10F)
Snackbar.make(view, "Aiming for $pagesPerLandscape pages at a time", Snackbar.LENGTH_LONG)
.setAction("Action", null)
.setAnchorView(R.id.zoom_out).show()
updatePresentationImages()
updatePresentations()
}
val drawerLayout: DrawerLayout = binding.drawerLayout
val navView: NavigationView = binding.navView
@ -207,6 +218,7 @@ class MainActivity : AppCompatActivity() {
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
updateDisplayText()
}
private fun handleIntent(intent: Intent?) {

View File

@ -7,7 +7,6 @@ import android.graphics.Rect
import android.graphics.RectF
import android.graphics.pdf.PdfRenderer
import android.os.ParcelFileDescriptor
import androidx.core.graphics.times
import java.io.File
import kotlin.math.max
import kotlin.math.min
@ -16,8 +15,7 @@ class PdfDocument(private val fileCached: File, private val autoCrop: Boolean =
private val pfd = ParcelFileDescriptor.open(fileCached, ParcelFileDescriptor.MODE_READ_ONLY)
private val renderer = PdfRenderer(pfd)
val bitmapThumbnails = mutableMapOf<Int, Bitmap>()
val bitmapPagesMain = mutableMapOf<Int, Bitmap>()
val bitmapPagesPresentation = mutableMapOf<Int, Bitmap>()
val bitmapPages = mutableMapOf<Pair<Int, Int>, MutableMap<Int, Bitmap>>() // bitmapPages[<maxWidthPx, maxHeightPx>][pageId]
private val pagePtWidths = mutableListOf<Int>()
private val pagePtHeights = mutableListOf<Int>()
private val pageAutoCropRects = mutableListOf<RectF>()
@ -57,11 +55,11 @@ class PdfDocument(private val fileCached: File, private val autoCrop: Boolean =
return bitmap
}
private fun renderPagesToMap(map: MutableMap<Int, Bitmap>, width: Int, height: Int, crop: Boolean = false) {
map.clear()
private fun renderPagesToMap(map: MutableMap<Int, Bitmap>, width: Int, height: Int, crop: Boolean = false, overwrite: Boolean = true) {
if (overwrite) map.clear()
val numPages = renderer.pageCount
for (i in 0..<numPages) {
map[i] = renderPage(i, width, height, crop)
map.computeIfAbsent(i) { renderPage(i, width, height, crop) }
}
}
@ -134,11 +132,12 @@ class PdfDocument(private val fileCached: File, private val autoCrop: Boolean =
}
}
fun renderPagesMain(width: Int, height: Int, crop: Boolean = true) {
renderPagesToMap(bitmapPagesMain, width, height, crop)
fun invalidateCache() {
bitmapPages.clear()
}
fun renderPagesPresentation(width: Int, height: Int, crop: Boolean = true) {
renderPagesToMap(bitmapPagesPresentation, width, height, crop)
fun renderPages(maxWidth: Int, maxHeight: Int, crop: Boolean = true) {
val key = Pair(maxWidth, maxHeight)
val map = bitmapPages.computeIfAbsent(key) { mutableMapOf<Int, Bitmap>() }
renderPagesToMap(map, maxWidth, maxHeight, crop, false)
}
}