Refactor control view to the template's GalleryFragment

If this works out, it will eventually be renamed appropriately
This commit is contained in:
Luke Hubmayer-Werner 2024-08-27 15:22:08 +09:30
parent f24522d728
commit 9ae2b36a3c
5 changed files with 177 additions and 154 deletions

View File

@ -13,6 +13,7 @@ import android.view.WindowManager
import android.widget.ImageView import android.widget.ImageView
import androidx.activity.SystemBarStyle import androidx.activity.SystemBarStyle
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import androidx.navigation.findNavController import androidx.navigation.findNavController
@ -26,6 +27,7 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import com.lhw.pdf.databinding.ActivityMainBinding import com.lhw.pdf.databinding.ActivityMainBinding
import com.lhw.pdf.ui.gallery.GalleryViewModel
import com.tom_roush.pdfbox.android.PDFBoxResourceLoader import com.tom_roush.pdfbox.android.PDFBoxResourceLoader
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
@ -34,6 +36,7 @@ import kotlin.math.max
import kotlin.math.min import kotlin.math.min
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private val viewModel: GalleryViewModel by viewModels()
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
@ -46,7 +49,6 @@ class MainActivity : AppCompatActivity() {
private val presentations = mutableMapOf<Int, MyPresentation>() 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 defaultCachedFileName = "cached.pdf" private val defaultCachedFileName = "cached.pdf"
private fun setPagesPerLandscape(pages: Float) { private fun setPagesPerLandscape(pages: Float) {
@ -118,35 +120,7 @@ class MainActivity : AppCompatActivity() {
presentation.show() presentation.show()
} }
private fun reLayoutThumbnails() {
val container = binding.appBarMain.contentMain.thumbnailsLayout
val disabledContainer = binding.appBarMain.contentMain.thumbnailsDisabledLayout
container.removeAllViewsInLayout()
disabledContainer.removeAllViewsInLayout()
showPages.map { if (it) container else disabledContainer }.zip(thumbnailImageViews).forEach { (cont, img) ->
cont.addView(img)
}
updatePresentations(false)
}
private fun populateThumbnails() {
showPages.clear()
for ((index, bitmap) in pdfDocument.bitmapThumbnails) {
val img = ImageView(this)
img.setImageBitmap(bitmap)
img.setOnClickListener {
showPages[index] = !showPages[index]
//img.setColorFilter(if (showPages[index]) 0 else 0x7F000000, PorterDuff.Mode.DARKEN)
reLayoutThumbnails()
}
thumbnailImageViews.add(img)
showPages.add(true)
}
reLayoutThumbnails()
}
private fun updateDisplayText() { private fun updateDisplayText() {
val displayText = binding.appBarMain.contentMain.textDisplays
val text = StringBuilder() val text = StringBuilder()
displayManager.displays.forEach { displayManager.displays.forEach {
@ -159,7 +133,7 @@ class MainActivity : AppCompatActivity() {
text.append("\n") text.append("\n")
} }
text.append(presentations) text.append(presentations)
displayText.text = text.toString() viewModel.textDisplays.value = text.toString()
} }
override fun onSaveInstanceState(outState: Bundle) { override fun onSaveInstanceState(outState: Bundle) {
@ -180,6 +154,11 @@ class MainActivity : AppCompatActivity() {
//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
savedInstanceState?.getFloat("thumbnailScrollProgress")?.let { viewModel.thumbnailScrollProgress.value = it }
savedInstanceState?.getFloat("pagesPerLandscape")?.let { pagesPerLandscape = it }
renderAutoCrop = savedInstanceState?.getBoolean("renderAutoCrop") ?: true
val lastFileName = savedInstanceState?.getString("fileName") // TODO: proper filenames
PDFBoxResourceLoader.init(this) PDFBoxResourceLoader.init(this)
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
@ -190,7 +169,6 @@ class MainActivity : AppCompatActivity() {
setSupportActionBar(binding.appBarMain.toolbar) setSupportActionBar(binding.appBarMain.toolbar)
// Load previous pdf, or included test pdf first // Load previous pdf, or included test pdf first
val lastFileName = savedInstanceState?.getString("fileName") // TODO: proper filenames
var file = File(cacheDir, defaultCachedFileName) var file = File(cacheDir, defaultCachedFileName)
if (!file.exists()) { if (!file.exists()) {
file = inputStreamToCache(defaultCachedFileName, resources.openRawResource(R.raw.testpdf)) file = inputStreamToCache(defaultCachedFileName, resources.openRawResource(R.raw.testpdf))
@ -199,45 +177,40 @@ class MainActivity : AppCompatActivity() {
// Then overwrite it with any pdf sent via intent // Then overwrite it with any pdf sent via intent
handleIntent(intent) handleIntent(intent)
renderAutoCrop = savedInstanceState?.getBoolean("renderAutoCrop") ?: true
pdfDocument.renderThumbnails(thumbnailWidth, thumbnailHeight) pdfDocument.renderThumbnails(thumbnailWidth, thumbnailHeight)
displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION).forEach(::makePresentationView) displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION).forEach(::makePresentationView)
populateThumbnails() // Populates showPages
val bitmaps = pdfDocument.bitmapThumbnails.toSortedMap().values.toList()
showPages.clear()
bitmaps.forEach { _ -> showPages.add(true) }
savedInstanceState?.getBooleanArray("showPages")?.forEachIndexed { i, show -> savedInstanceState?.getBooleanArray("showPages")?.forEachIndexed { i, show ->
showPages[i] = show showPages[i] = show
} }
reLayoutThumbnails() viewModel.showPages.value = showPages.toList()
viewModel.pageThumbnails.value = bitmaps
val container = binding.appBarMain.contentMain.thumbnailsLayout
val containerScroll = binding.appBarMain.contentMain.thumbnailsScroll
containerScroll.viewTreeObserver.addOnScrollChangedListener {
thumbnailScrollProgress = containerScroll.scrollX.toFloat() / (container.width - containerScroll.width)
presentations.values.forEach {it.setScrollProgress(thumbnailScrollProgress)}
}
savedInstanceState?.getFloat("thumbnailScrollProgress")?.let { thumbnailScrollProgress = it }
containerScroll.scrollTo((thumbnailScrollProgress * (container.width - containerScroll.width)).toInt(), 0)
savedInstanceState?.getFloat("pagesPerLandscape")?.let { pagesPerLandscape = it } viewModel.pagesPerLandscape.observe(this) {
val pageZoomLevels = arrayOf(1F, 1.5F, 2F, 2.5F, 3F, 3.5F, 4F, 5F, 6F, 8F, 10F) setPagesPerLandscape(it)
val chipGroupPages = binding.appBarMain.contentMain.chipGroupPages
pageZoomLevels.forEach { pages ->
val chip = Chip(this)
chip.text = "$pages pages"
chip.isCheckable = true
chip.isChecked = (pages == pagesPerLandscape)
chip.setOnCheckedChangeListener { _, checked -> if (checked) setPagesPerLandscape(pages) }
chipGroupPages.addView(chip)
} }
binding.appBarMain.contentMain.chipAutoCrop.setOnCheckedChangeListener { _, checked -> viewModel.renderAutoCrop.observe(this) {
renderAutoCrop = checked renderAutoCrop = it
updatePresentations() updatePresentations()
} }
binding.appBarMain.contentMain.chipUsePdfBox.setOnCheckedChangeListener { _, checked -> viewModel.usePdfBox.observe(this) {
usePdfBox = checked usePdfBox = it
pdfDocument.usePdfBox = usePdfBox pdfDocument.usePdfBox = usePdfBox
updatePresentations() updatePresentations()
} }
viewModel.showPages.observe(this) {
it.forEachIndexed() {i, shown -> showPages[i] = shown}
updatePresentations(false)
}
viewModel.thumbnailScrollProgress.observe(this) { thumbnailScrollProgress ->
presentations.values.forEach {it.setScrollProgress(thumbnailScrollProgress)}
}
val drawerLayout: DrawerLayout = binding.drawerLayout val drawerLayout: DrawerLayout = binding.drawerLayout
val navView: NavigationView = binding.navView val navView: NavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_content_main) val navController = findNavController(R.id.nav_host_fragment_content_main)

View File

@ -1,36 +1,70 @@
package com.lhw.pdf.ui.gallery package com.lhw.pdf.ui.gallery
import android.graphics.Bitmap
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.google.android.material.chip.Chip
import com.lhw.pdf.databinding.FragmentGalleryBinding import com.lhw.pdf.databinding.FragmentGalleryBinding
class GalleryFragment : Fragment() { class GalleryFragment : Fragment() {
private val galleryViewModel: GalleryViewModel by activityViewModels()
private var _binding: FragmentGalleryBinding? = null private var _binding: FragmentGalleryBinding? = null
// This property is only valid between onCreateView and // This property is only valid between onCreateView and
// onDestroyView. // onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
private val pageZoomLevels = arrayOf(1F, 1.5F, 2F, 2.5F, 3F, 3.5F, 4F, 5F, 6F, 8F, 10F)
private val pageZoomLevelChips = mutableMapOf<Float, Chip>()
private val thumbnailImageViews = mutableListOf<ImageView>()
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
val galleryViewModel = // val galleryViewModel = ViewModelProvider(this)[GalleryViewModel::class.java]
ViewModelProvider(this)[GalleryViewModel::class.java]
_binding = FragmentGalleryBinding.inflate(inflater, container, false) _binding = FragmentGalleryBinding.inflate(inflater, container, false)
val root: View = binding.root val root: View = binding.root
val textView: TextView = binding.textGallery val chipGroupPages = binding.chipGroupPages
galleryViewModel.text.observe(viewLifecycleOwner) { pageZoomLevels.forEach { pages ->
textView.text = it val chip = Chip(context)
chip.text = "$pages pages"
chip.isCheckable = true
chip.setOnCheckedChangeListener { _, checked -> if (checked) galleryViewModel.pagesPerLandscape.value = pages }
chipGroupPages.addView(chip)
pageZoomLevelChips[pages] = chip
}
pageZoomLevelChips[galleryViewModel.pagesPerLandscape.value]?.isChecked = true
binding.chipAutoCrop.isChecked = galleryViewModel.renderAutoCrop.value!!
binding.chipUsePdfBox.isChecked = galleryViewModel.usePdfBox.value!!
galleryViewModel.textDisplays.observe(viewLifecycleOwner) { binding.textDisplays.text = it }
galleryViewModel.usePdfBox.observe(viewLifecycleOwner) { binding.chipUsePdfBox.isChecked = it }
galleryViewModel.renderAutoCrop.observe(viewLifecycleOwner) { binding.chipAutoCrop.isChecked = it }
galleryViewModel.pagesPerLandscape.observe(viewLifecycleOwner) {
pageZoomLevelChips[it]?.isChecked = true
}
binding.chipAutoCrop.setOnCheckedChangeListener { _, checked -> galleryViewModel.renderAutoCrop.value = checked }
binding.chipUsePdfBox.setOnCheckedChangeListener { _, checked -> galleryViewModel.usePdfBox.value = checked }
galleryViewModel.pageThumbnails.observe(viewLifecycleOwner, ::populateThumbnails)
val container = binding.thumbnailsLayout
val containerScroll = binding.thumbnailsScroll
containerScroll.scrollTo(((galleryViewModel.thumbnailScrollProgress.value ?: 0F) * (container.width - containerScroll.width)).toInt(), 0)
containerScroll.viewTreeObserver.addOnScrollChangedListener {
galleryViewModel.thumbnailScrollProgress.value = containerScroll.scrollX.toFloat() / (container.width - containerScroll.width)
} }
return root return root
} }
@ -39,4 +73,31 @@ class GalleryFragment : Fragment() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
private fun reLayoutThumbnails() {
val container = binding.thumbnailsLayout
val disabledContainer = binding.thumbnailsDisabledLayout
container.removeAllViewsInLayout()
disabledContainer.removeAllViewsInLayout()
galleryViewModel.showPages.value
?.map { if (it) container else disabledContainer }
?.zip(thumbnailImageViews)?.forEach { (cont, img) ->
cont.addView(img)
}
}
private fun populateThumbnails(bitmaps: List<Bitmap>) {
bitmaps.forEachIndexed() { index, bitmap ->
val img = ImageView(context)
img.setImageBitmap(bitmap)
img.setOnClickListener {
val showPages = galleryViewModel.showPages.value!!.toMutableList()
showPages[index] = !showPages[index]
galleryViewModel.showPages.value = showPages.toList()
reLayoutThumbnails()
}
thumbnailImageViews.add(img)
}
reLayoutThumbnails()
}
} }

View File

@ -1,13 +1,16 @@
package com.lhw.pdf.ui.gallery package com.lhw.pdf.ui.gallery
import android.graphics.Bitmap
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
class GalleryViewModel : ViewModel() { class GalleryViewModel : ViewModel() {
val pageThumbnails = MutableLiveData<List<Bitmap>>().apply { value = listOf() }
private val _text = MutableLiveData<String>().apply { val showPages = MutableLiveData<List<Boolean>>().apply { value = listOf() }
value = "This is gallery Fragment" val thumbnailScrollProgress = MutableLiveData<Float>().apply { value = 0F }
} val textDisplays = MutableLiveData<String>().apply { value = "" }
val text: LiveData<String> = _text val pagesPerLandscape = MutableLiveData<Float>().apply { value = 3F }
val renderAutoCrop = MutableLiveData<Boolean>().apply { value = true }
val usePdfBox = MutableLiveData<Boolean>().apply { value = false }
} }

View File

@ -18,79 +18,4 @@
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/mobile_navigation" /> app:navGraph="@navigation/mobile_navigation" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<HorizontalScrollView
android:id="@+id/thumbnails_disabled_scroll"
android:layout_width="match_parent"
android:layout_height="300dp"
android:background="#80C01010">
<LinearLayout
android:id="@+id/thumbnails_disabled_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:showIn="@layout/content_main" />
</HorizontalScrollView>
<HorizontalScrollView
android:id="@+id/thumbnails_scroll"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3">
<LinearLayout
android:id="@+id/thumbnails_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:showIn="@layout/content_main" />
</HorizontalScrollView>
<TextView
android:id="@+id/text_displays"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/chip_group_pages"
app:singleSelection="true"
app:selectionRequired="true"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</com.google.android.material.chip.ChipGroup>
<com.google.android.material.chip.ChipGroup
android:id="@+id/chip_group_toggles"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.google.android.material.chip.Chip
android:id="@+id/chip_auto_crop"
android:checkable="true"
android:checked="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Auto Crop" />
<com.google.android.material.chip.Chip
android:id="@+id/chip_use_pdf_box"
android:checkable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Use PdfBox renderer (slower)" />
</com.google.android.material.chip.ChipGroup>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -6,17 +6,78 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context=".ui.gallery.GalleryFragment"> tools:context=".ui.gallery.GalleryFragment">
<TextView <LinearLayout
android:id="@+id/text_gallery"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:layout_marginStart="8dp" android:orientation="vertical">
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp" <HorizontalScrollView
android:textAlignment="center" android:id="@+id/thumbnails_disabled_scroll"
android:textSize="20sp" android:layout_width="match_parent"
app:layout_constraintBottom_toBottomOf="parent" android:layout_height="300dp"
app:layout_constraintEnd_toEndOf="parent" android:background="#80C01010">
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> <LinearLayout
android:id="@+id/thumbnails_disabled_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:showIn="@layout/content_main" />
</HorizontalScrollView>
<HorizontalScrollView
android:id="@+id/thumbnails_scroll"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3">
<LinearLayout
android:id="@+id/thumbnails_layout"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
tools:showIn="@layout/content_main" />
</HorizontalScrollView>
<TextView
android:id="@+id/text_displays"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="center"
android:textSize="20sp" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/chip_group_pages"
app:singleSelection="true"
app:selectionRequired="true"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
</com.google.android.material.chip.ChipGroup>
<com.google.android.material.chip.ChipGroup
android:id="@+id/chip_group_toggles"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1">
<com.google.android.material.chip.Chip
android:id="@+id/chip_auto_crop"
android:checkable="true"
android:checked="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Auto Crop" />
<com.google.android.material.chip.Chip
android:id="@+id/chip_use_pdf_box"
android:checkable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Use PdfBox renderer (slower)" />
</com.google.android.material.chip.ChipGroup>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>