Dynamically create Presentations when displays are added/removed
This commit is contained in:
parent
f9b7db0e44
commit
96404488f1
|
@ -5,9 +5,9 @@ import android.content.Intent
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
import android.hardware.display.DisplayManager
|
import android.hardware.display.DisplayManager
|
||||||
import android.hardware.display.DisplayManager.DisplayListener
|
import android.hardware.display.DisplayManager.DisplayListener
|
||||||
import android.media.MediaRouter
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.DisplayMetrics
|
import android.util.DisplayMetrics
|
||||||
|
import android.view.Display
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
|
@ -33,12 +33,11 @@ class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||||
private lateinit var binding: ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
private lateinit var displayManager: DisplayManager
|
private lateinit var displayManager: DisplayManager
|
||||||
private lateinit var mediaRouter: MediaRouter
|
|
||||||
private val thumbnailWidth = 500
|
private val thumbnailWidth = 500
|
||||||
private val thumbnailHeight = 700
|
private val thumbnailHeight = 700
|
||||||
private var renderAutoCrop = true
|
private var renderAutoCrop = true
|
||||||
private var pagesPerLandscape = 3F
|
private var pagesPerLandscape = 3F
|
||||||
private var presentation: MyPresentation? = null
|
private val presentations = mutableMapOf<Int, MyPresentation>()
|
||||||
private lateinit var pdfDocument: PdfDocument
|
private lateinit var pdfDocument: PdfDocument
|
||||||
private val showPages = mutableListOf<Boolean>()
|
private val showPages = mutableListOf<Boolean>()
|
||||||
private val thumbnailImageViews = mutableListOf<ImageView>()
|
private val thumbnailImageViews = mutableListOf<ImageView>()
|
||||||
|
@ -46,18 +45,27 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private val displayListener = object: DisplayListener {
|
private val displayListener = object: DisplayListener {
|
||||||
override fun onDisplayAdded(p0: Int) {
|
override fun onDisplayAdded(p0: Int) {
|
||||||
updateDisplayText()
|
reevaluateDisplay(p0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisplayChanged(p0: Int) {
|
override fun onDisplayChanged(p0: Int) {
|
||||||
updateDisplayText()
|
reevaluateDisplay(p0)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDisplayRemoved(p0: Int) {
|
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 {
|
private fun inputStreamToCache(outputFilename: String, inputStream: InputStream): File {
|
||||||
val fileCached = File(cacheDir, outputFilename)
|
val fileCached = File(cacheDir, outputFilename)
|
||||||
val output = FileOutputStream(fileCached)
|
val output = FileOutputStream(fileCached)
|
||||||
|
@ -69,30 +77,34 @@ class MainActivity : AppCompatActivity() {
|
||||||
return fileCached
|
return fileCached
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePresentationImages(reRender: Boolean = true) {
|
private fun updateBitmaps(maxWidth: Int, maxHeight: Int) {
|
||||||
presentation?.let { p ->
|
println("Rendering pages with maxHeight=$maxHeight maxWidth=$maxWidth")
|
||||||
if (reRender) {
|
pdfDocument.renderPages(maxWidth, maxHeight, renderAutoCrop)
|
||||||
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 makePresentationView() {
|
private fun updatePresentationImages(p: MyPresentation) {
|
||||||
presentation = null
|
val maxHeight: Int = p.displayMetrics.heightPixels
|
||||||
mediaRouter.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_VIDEO)?.presentationDisplay?.let { display ->
|
val maxWidth: Int = min(maxHeight * 5 / 7, (p.displayMetrics.widthPixels / pagesPerLandscape).toInt())
|
||||||
presentation = MyPresentation(this, display)
|
updateBitmaps(maxWidth, maxHeight)
|
||||||
updatePresentationImages()
|
val key = Pair(maxWidth, maxHeight)
|
||||||
presentation?.setOnCancelListener { presentation = null }
|
val bitmaps = showPages.withIndex()
|
||||||
presentation?.show()
|
.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() {
|
private fun reLayoutThumbnails() {
|
||||||
|
@ -103,11 +115,10 @@ class MainActivity : AppCompatActivity() {
|
||||||
showPages.map { if (it) container else disabledContainer }.zip(thumbnailImageViews).forEach { (cont, img) ->
|
showPages.map { if (it) container else disabledContainer }.zip(thumbnailImageViews).forEach { (cont, img) ->
|
||||||
cont.addView(img)
|
cont.addView(img)
|
||||||
}
|
}
|
||||||
updatePresentationImages(false)
|
updatePresentations(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun populateThumbnails() {
|
private fun populateThumbnails() {
|
||||||
val container = binding.appBarMain.contentMain.thumbnailsLayout
|
|
||||||
for ((index, bitmap) in pdfDocument.bitmapThumbnails) {
|
for ((index, bitmap) in pdfDocument.bitmapThumbnails) {
|
||||||
val img = ImageView(this)
|
val img = ImageView(this)
|
||||||
img.setImageBitmap(bitmap)
|
img.setImageBitmap(bitmap)
|
||||||
|
@ -125,7 +136,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
private fun updateDisplayText() {
|
private fun updateDisplayText() {
|
||||||
val displayText = binding.appBarMain.contentMain.textDisplays
|
val displayText = binding.appBarMain.contentMain.textDisplays
|
||||||
val text = StringBuilder()
|
val text = StringBuilder()
|
||||||
//displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION).forEach {
|
|
||||||
displayManager.displays.forEach {
|
displayManager.displays.forEach {
|
||||||
val displayMetrics = DisplayMetrics()
|
val displayMetrics = DisplayMetrics()
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
|
@ -135,6 +146,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
text.append(s)
|
text.append(s)
|
||||||
text.append("\n")
|
text.append("\n")
|
||||||
}
|
}
|
||||||
|
text.append(presentations)
|
||||||
displayText.text = text.toString()
|
displayText.text = text.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,14 +154,12 @@ class MainActivity : AppCompatActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||||
displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
displayManager = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
|
||||||
mediaRouter = getSystemService(Context.MEDIA_ROUTER_SERVICE) as MediaRouter
|
|
||||||
//val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
|
//val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
|
||||||
//windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()) //Doesn't seem to actually fill the vacant space by default
|
//windowInsetsController.hide(WindowInsetsCompat.Type.systemBars()) //Doesn't seem to actually fill the vacant space by default
|
||||||
|
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
|
|
||||||
updateDisplayText()
|
|
||||||
displayManager.registerDisplayListener(displayListener, null)
|
displayManager.registerDisplayListener(displayListener, null)
|
||||||
|
|
||||||
setSupportActionBar(binding.appBarMain.toolbar)
|
setSupportActionBar(binding.appBarMain.toolbar)
|
||||||
|
@ -164,13 +174,14 @@ class MainActivity : AppCompatActivity() {
|
||||||
handleIntent(intent)
|
handleIntent(intent)
|
||||||
|
|
||||||
pdfDocument.renderThumbnails(thumbnailWidth, thumbnailHeight)
|
pdfDocument.renderThumbnails(thumbnailWidth, thumbnailHeight)
|
||||||
makePresentationView()
|
displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION).forEach(::makePresentationView)
|
||||||
populateThumbnails()
|
populateThumbnails()
|
||||||
|
|
||||||
val container = binding.appBarMain.contentMain.thumbnailsLayout
|
val container = binding.appBarMain.contentMain.thumbnailsLayout
|
||||||
val containerScroll = binding.appBarMain.contentMain.thumbnailsScroll
|
val containerScroll = binding.appBarMain.contentMain.thumbnailsScroll
|
||||||
containerScroll.viewTreeObserver.addOnScrollChangedListener {
|
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 ->
|
binding.appBarMain.crop.setOnClickListener { view ->
|
||||||
|
@ -179,21 +190,21 @@ class MainActivity : AppCompatActivity() {
|
||||||
Snackbar.make(view, "Toggling Auto Crop $s", Snackbar.LENGTH_LONG)
|
Snackbar.make(view, "Toggling Auto Crop $s", Snackbar.LENGTH_LONG)
|
||||||
.setAction("Action", null)
|
.setAction("Action", null)
|
||||||
.setAnchorView(R.id.crop).show()
|
.setAnchorView(R.id.crop).show()
|
||||||
updatePresentationImages()
|
updatePresentations()
|
||||||
}
|
}
|
||||||
binding.appBarMain.zoomIn.setOnClickListener { view ->
|
binding.appBarMain.zoomIn.setOnClickListener { view ->
|
||||||
pagesPerLandscape = max(pagesPerLandscape - 0.5F, 1F)
|
pagesPerLandscape = max(pagesPerLandscape - 0.5F, 1F)
|
||||||
Snackbar.make(view, "Aiming for $pagesPerLandscape pages at a time", Snackbar.LENGTH_LONG)
|
Snackbar.make(view, "Aiming for $pagesPerLandscape pages at a time", Snackbar.LENGTH_LONG)
|
||||||
.setAction("Action", null)
|
.setAction("Action", null)
|
||||||
.setAnchorView(R.id.zoom_in).show()
|
.setAnchorView(R.id.zoom_in).show()
|
||||||
updatePresentationImages()
|
updatePresentations()
|
||||||
}
|
}
|
||||||
binding.appBarMain.zoomOut.setOnClickListener { view ->
|
binding.appBarMain.zoomOut.setOnClickListener { view ->
|
||||||
pagesPerLandscape = min(pagesPerLandscape + 0.5F, 10F)
|
pagesPerLandscape = min(pagesPerLandscape + 0.5F, 10F)
|
||||||
Snackbar.make(view, "Aiming for $pagesPerLandscape pages at a time", Snackbar.LENGTH_LONG)
|
Snackbar.make(view, "Aiming for $pagesPerLandscape pages at a time", Snackbar.LENGTH_LONG)
|
||||||
.setAction("Action", null)
|
.setAction("Action", null)
|
||||||
.setAnchorView(R.id.zoom_out).show()
|
.setAnchorView(R.id.zoom_out).show()
|
||||||
updatePresentationImages()
|
updatePresentations()
|
||||||
}
|
}
|
||||||
val drawerLayout: DrawerLayout = binding.drawerLayout
|
val drawerLayout: DrawerLayout = binding.drawerLayout
|
||||||
val navView: NavigationView = binding.navView
|
val navView: NavigationView = binding.navView
|
||||||
|
@ -207,6 +218,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
)
|
)
|
||||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||||
navView.setupWithNavController(navController)
|
navView.setupWithNavController(navController)
|
||||||
|
updateDisplayText()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleIntent(intent: Intent?) {
|
private fun handleIntent(intent: Intent?) {
|
||||||
|
|
|
@ -7,7 +7,6 @@ import android.graphics.Rect
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
import android.graphics.pdf.PdfRenderer
|
import android.graphics.pdf.PdfRenderer
|
||||||
import android.os.ParcelFileDescriptor
|
import android.os.ParcelFileDescriptor
|
||||||
import androidx.core.graphics.times
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
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 pfd = ParcelFileDescriptor.open(fileCached, ParcelFileDescriptor.MODE_READ_ONLY)
|
||||||
private val renderer = PdfRenderer(pfd)
|
private val renderer = PdfRenderer(pfd)
|
||||||
val bitmapThumbnails = mutableMapOf<Int, Bitmap>()
|
val bitmapThumbnails = mutableMapOf<Int, Bitmap>()
|
||||||
val bitmapPagesMain = mutableMapOf<Int, Bitmap>()
|
val bitmapPages = mutableMapOf<Pair<Int, Int>, MutableMap<Int, Bitmap>>() // bitmapPages[<maxWidthPx, maxHeightPx>][pageId]
|
||||||
val bitmapPagesPresentation = mutableMapOf<Int, Bitmap>()
|
|
||||||
private val pagePtWidths = mutableListOf<Int>()
|
private val pagePtWidths = mutableListOf<Int>()
|
||||||
private val pagePtHeights = mutableListOf<Int>()
|
private val pagePtHeights = mutableListOf<Int>()
|
||||||
private val pageAutoCropRects = mutableListOf<RectF>()
|
private val pageAutoCropRects = mutableListOf<RectF>()
|
||||||
|
@ -57,11 +55,11 @@ class PdfDocument(private val fileCached: File, private val autoCrop: Boolean =
|
||||||
return bitmap
|
return bitmap
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun renderPagesToMap(map: MutableMap<Int, Bitmap>, width: Int, height: Int, crop: Boolean = false) {
|
private fun renderPagesToMap(map: MutableMap<Int, Bitmap>, width: Int, height: Int, crop: Boolean = false, overwrite: Boolean = true) {
|
||||||
map.clear()
|
if (overwrite) map.clear()
|
||||||
val numPages = renderer.pageCount
|
val numPages = renderer.pageCount
|
||||||
for (i in 0..<numPages) {
|
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) {
|
fun invalidateCache() {
|
||||||
renderPagesToMap(bitmapPagesMain, width, height, crop)
|
bitmapPages.clear()
|
||||||
}
|
}
|
||||||
|
fun renderPages(maxWidth: Int, maxHeight: Int, crop: Boolean = true) {
|
||||||
fun renderPagesPresentation(width: Int, height: Int, crop: Boolean = true) {
|
val key = Pair(maxWidth, maxHeight)
|
||||||
renderPagesToMap(bitmapPagesPresentation, width, height, crop)
|
val map = bitmapPages.computeIfAbsent(key) { mutableMapOf<Int, Bitmap>() }
|
||||||
|
renderPagesToMap(map, maxWidth, maxHeight, crop, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue