Accept intents to load a single PDF
Also refactor Presentation
This commit is contained in:
parent
75be87a8fe
commit
7cf7731541
|
@ -4,7 +4,9 @@ Minimum Viable Goals:
|
|||
- [x] Presentation View - i.e. show on an external monitor - haven't seen another app do this yet!
|
||||
- [x] Immersive View - no system status bar etc.
|
||||
- [x] Autocrop margins
|
||||
- [ ] Open files the Android way - currently just a bundled test file
|
||||
- [ ] Open files the Android way
|
||||
- - [x] Accept intents to load a single PDF
|
||||
- - [ ] Built-in file browser with requisite permissions
|
||||
- [ ] Delete+Reorder Pages - doesn't have to save the source file, but does need to save a change journal
|
||||
- - [x] Tap thumbnails to hide/show pages
|
||||
- - [ ] Drag thumbnails to rearrange pages
|
||||
|
|
|
@ -16,12 +16,17 @@
|
|||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:launchMode="singleInstance"
|
||||
android:theme="@style/Theme.PDFcast.NoActionBar">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
<intent-filter android:label="Read PDF">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="application/pdf" android:scheme="content" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
package com.lhw.pdf
|
||||
|
||||
import android.app.Presentation
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.PorterDuff
|
||||
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.HorizontalScrollView
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import androidx.navigation.findNavController
|
||||
|
@ -33,15 +28,15 @@ import kotlin.math.min
|
|||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
private lateinit var mediaRouter: MediaRouter
|
||||
private val thumbnailWidth = 500
|
||||
private val thumbnailHeight = 700
|
||||
private var renderAutoCrop = true
|
||||
private var pagesPerLandscape = 3F
|
||||
private val presentationDisplayMetrics = DisplayMetrics()
|
||||
private var presentation: MyPresentation? = null
|
||||
private lateinit var pdfDocument: PdfDocument
|
||||
private lateinit var presentationScroll: HorizontalScrollView
|
||||
private lateinit var presentationLayout: LinearLayout
|
||||
private val showPages = mutableListOf<Boolean>()
|
||||
private val defaultCachedFileName = "cached.pdf"
|
||||
|
||||
private fun inputStreamToCache(outputFilename: String, inputStream: InputStream): File {
|
||||
val fileCached = File(cacheDir, outputFilename)
|
||||
|
@ -54,66 +49,31 @@ class MainActivity : AppCompatActivity() {
|
|||
return fileCached
|
||||
}
|
||||
|
||||
private fun initializePdfDocument() {
|
||||
val inputStream = resources.openRawResource(R.raw.testpdf)
|
||||
pdfDocument = PdfDocument(inputStreamToCache("cached.pdf", inputStream), true)
|
||||
}
|
||||
|
||||
class MyPresentation(outerContext: Context, display: Display) : Presentation(outerContext, display) {
|
||||
|
||||
}
|
||||
|
||||
private fun updatePresentationImages(rerender: Boolean = true) {
|
||||
if (rerender) {
|
||||
val maxHeight: Int = presentationDisplayMetrics.heightPixels
|
||||
val maxWidth: Int = min(maxHeight*5/7, (presentationDisplayMetrics.widthPixels/pagesPerLandscape).toInt())
|
||||
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)
|
||||
}
|
||||
presentationLayout.removeAllViewsInLayout()
|
||||
showPages.withIndex().filter { (i, show) -> (show) }.forEach { (i, show) ->
|
||||
val bitmap = pdfDocument.bitmapPagesPresentation[i]
|
||||
val img = ImageView(this)
|
||||
img.setImageBitmap(bitmap)
|
||||
presentationLayout.addView(img)
|
||||
val bitmaps = showPages.withIndex()
|
||||
.mapNotNull { (i, show) -> if (show) pdfDocument.bitmapPagesPresentation[i] else null }
|
||||
.toList()
|
||||
p.updateImages(bitmaps)
|
||||
}
|
||||
}
|
||||
|
||||
private fun presentationView() {
|
||||
val mediaRouter = getSystemService(Context.MEDIA_ROUTER_SERVICE) as MediaRouter
|
||||
val presentationDisplay = mediaRouter.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_VIDEO)?.presentationDisplay
|
||||
if (presentationDisplay != null) {
|
||||
val presentation = MyPresentation(this, presentationDisplay)
|
||||
when (val pw = presentation.window) {
|
||||
null -> {}
|
||||
else -> {
|
||||
WindowCompat.getInsetsController(pw, pw.decorView).hide(WindowInsetsCompat.Type.systemBars())
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
presentationDisplay.getMetrics(presentationDisplayMetrics)
|
||||
presentationLayout = LinearLayout(presentation.context)
|
||||
presentationScroll = HorizontalScrollView(presentation.context)
|
||||
presentationScroll.addView(presentationLayout)
|
||||
val layoutParams = WindowManager.LayoutParams()
|
||||
presentation.addContentView(presentationScroll, layoutParams)
|
||||
private fun makePresentationView() {
|
||||
mediaRouter.getSelectedRoute(MediaRouter.ROUTE_TYPE_LIVE_VIDEO)?.presentationDisplay?.let { display ->
|
||||
presentation = MyPresentation(this, display)
|
||||
updatePresentationImages()
|
||||
presentation.show()
|
||||
presentation?.show()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val windowInsetsController = WindowCompat.getInsetsController(window, window.decorView)
|
||||
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
setSupportActionBar(binding.appBarMain.toolbar)
|
||||
|
||||
initializePdfDocument()
|
||||
pdfDocument.renderThumbnails(thumbnailWidth, thumbnailHeight)
|
||||
private fun populateThumbnails() {
|
||||
val container = binding.appBarMain.contentMain.thumbnailsLayout
|
||||
for ((index, bitmap) in pdfDocument.bitmapThumbnails) {
|
||||
val img = ImageView(this)
|
||||
|
@ -126,14 +86,36 @@ class MainActivity : AppCompatActivity() {
|
|||
container.addView(img)
|
||||
showPages.add(true)
|
||||
}
|
||||
presentationView()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
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)
|
||||
|
||||
setSupportActionBar(binding.appBarMain.toolbar)
|
||||
|
||||
// Load previous pdf, or included test pdf first
|
||||
var file = File(cacheDir, defaultCachedFileName)
|
||||
if (!file.exists()) {
|
||||
file = inputStreamToCache(defaultCachedFileName, resources.openRawResource(R.raw.testpdf))
|
||||
}
|
||||
pdfDocument = PdfDocument(file, true)
|
||||
// Then overwrite it with any pdf sent via intent
|
||||
handleIntent(intent)
|
||||
|
||||
pdfDocument.renderThumbnails(thumbnailWidth, thumbnailHeight)
|
||||
populateThumbnails()
|
||||
makePresentationView()
|
||||
|
||||
val container = binding.appBarMain.contentMain.thumbnailsLayout
|
||||
val containerScroll = binding.appBarMain.contentMain.thumbnailsScroll
|
||||
containerScroll.viewTreeObserver.addOnScrollChangedListener {
|
||||
presentationScroll.scrollTo(
|
||||
containerScroll.scrollX * (presentationLayout.width - presentationScroll.width) / (container.width - containerScroll.width),
|
||||
0
|
||||
)
|
||||
presentation?.setScrollProgress(containerScroll.scrollX.toFloat() / (container.width - containerScroll.width))
|
||||
}
|
||||
|
||||
binding.appBarMain.crop.setOnClickListener { view ->
|
||||
|
@ -172,6 +154,22 @@ class MainActivity : AppCompatActivity() {
|
|||
navView.setupWithNavController(navController)
|
||||
}
|
||||
|
||||
private fun handleIntent(intent: Intent?) {
|
||||
val uri = intent?.data
|
||||
if (uri == null) {
|
||||
println("null intent uri passed")
|
||||
return
|
||||
}
|
||||
println("intent uri passed: $uri")
|
||||
// Do something with the URI to load PDF file
|
||||
pdfDocument = contentResolver.openInputStream(uri)?.let { PdfDocument(inputStreamToCache(defaultCachedFileName, it), true) } ?: pdfDocument
|
||||
}
|
||||
|
||||
override fun onNewIntent(intent: Intent?) {
|
||||
super.onNewIntent(intent)
|
||||
handleIntent(intent)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
// Inflate the menu; this adds items to the action bar if it is present.
|
||||
menuInflater.inflate(R.menu.main, menu)
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package com.lhw.pdf
|
||||
|
||||
import android.app.Presentation
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.Display
|
||||
import android.view.WindowManager
|
||||
import android.widget.HorizontalScrollView
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
|
||||
class MyPresentation(outerContext: Context, display: Display) : Presentation(outerContext, display) {
|
||||
val displayMetrics = DisplayMetrics()
|
||||
private val layout = LinearLayout(this.context)
|
||||
private val scroll = HorizontalScrollView(this.context)
|
||||
private val layoutParams = WindowManager.LayoutParams()
|
||||
init {
|
||||
@Suppress("DEPRECATION")
|
||||
display.getMetrics(displayMetrics)
|
||||
window?.let {
|
||||
WindowCompat.getInsetsController(it, it.decorView)
|
||||
.hide(WindowInsetsCompat.Type.systemBars())
|
||||
}
|
||||
scroll.addView(layout)
|
||||
println(layoutParams)
|
||||
addContentView(scroll, layoutParams)
|
||||
}
|
||||
|
||||
fun updateImages(bitmaps: Iterable<Bitmap>) {
|
||||
println("Updating presentation images")
|
||||
println("Layout has scale ${layout.scaleX} ${layout.scaleY}")
|
||||
layout.removeAllViewsInLayout()
|
||||
bitmaps.forEach {
|
||||
val img = ImageView(this.context)
|
||||
img.setImageBitmap(it)
|
||||
layout.addView(img)
|
||||
img.minimumWidth = it.width // No idea what broke when I refactored this class
|
||||
img.minimumHeight = it.height
|
||||
println("${it.width}x${it.height}")
|
||||
}
|
||||
println("Presentation heights ${scroll.height} ${layout.height}")
|
||||
}
|
||||
|
||||
fun setScrollProgress(progress: Float) {
|
||||
scroll.scrollTo((progress * (layout.width - scroll.width)).toInt(), 0)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue