なるほどね!でも・・ベストの中のベスト、これは絶対に外さない!って画像の場合、PDFにまとめてしまって管理したい・・そう思ったことはないですか?どっちでも良いですか?でも、PDF管理には明確なメリットがありまーす!
それはファイルの移動。ファイルの移動では細かいファイルが多数ある場合、極端にコピー・移動速度が落ちてしまうというデメリットがあります!(経験者ならば分かりますね?)。PDF形式でまとめてしまってれば、これを防ぐことが出来る(ハズ)なのです。
ところが意外なことにPDFファイルをスライドショーのように、任意のインターバルでフリーハンド(両手が使えるに越したことはないからね!)で流す事が出来るソフトが、ホントーッに無いのです。嘘でしょ?このアプリ大氾濫の時代に?
package com.example.pdfslideshow
import android.graphics.pdf.PdfRenderer
import android.os.ParcelFileDescriptor
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import java.io.IOException
class MainActivity : AppCompatActivity() {
private lateinit var pdfImageView: ImageView
private lateinit var btnOpenPdf: Button
private lateinit var etInterval: EditText
private lateinit var btnToggleSlideshow: Button
private var pdfRenderer: PdfRenderer? = null
private var currentPdfDescriptor: ParcelFileDescriptor? = null
private var currentPageIndex = 0
private var totalPages = 0
private var isSlideshowRunning = false
private val handler = Handler(Looper.getMainLooper())
private var slideshowRunnable: Runnable? = null
private val openPdfLauncher = registerForActivityResult
(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
result.data?.data?.let { uri ->
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
pdfImageView = findViewById(R.id.pdfImageView)
btnOpenPdf = findViewById(R.id.btnOpenPdf)
etInterval = findViewById(R.id.etInterval)
btnToggleSlideshow = findViewById(R.id.btnToggleSlideshow)
btnOpenPdf.setOnClickListener {
stopSlideshow()
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/pdf"
}
openPdfLauncher.launch(intent)
}
btnToggleSlideshow.setOnClickListener {
if (isSlideshowRunning) {
stopSlideshow()
} else {
startSlideshow()
}
}
}
private fun openPdf(uri: Uri) {
try {
closePdf() // Close previous if any
val fileDescriptor = contentResolver.openFileDescriptor(uri, "r")
if (fileDescriptor != null) {
currentPdfDescriptor = fileDescriptor
pdfRenderer = PdfRenderer(fileDescriptor)
totalPages = pdfRenderer?.pageCount ?: 0
currentPageIndex = 0
if (totalPages > 0) {
showPage(currentPageIndex)
btnToggleSlideshow.isEnabled = true
Toast.makeText(this, "PDF Loaded: $totalPages pages",
Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "PDF has 0 pages",
Toast.LENGTH_SHORT).show()
}
}
} catch (e: IOException) {
e.printStackTrace()
Toast.makeText(this, "Error opening PDF", Toast.LENGTH_SHORT).show()
}
}
private fun showPage(index: Int) {
if (pdfRenderer == null || totalPages == 0) return
val page = pdfRenderer!!.openPage(index)
// Create a bitmap with the page's dimensions
val bitmap = Bitmap.createBitmap(page.width, page.height,
Bitmap.Config.ARGB_8888)
// Render the page onto the bitmap
page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
// Display the bitmap
pdfImageView.setImageBitmap(bitmap)
page.close()
}
private fun startSlideshow() {
val intervalStr = etInterval.text.toString()
val intervalSeconds = intervalStr.toLongOrNull() ?: 3L
val intervalMillis = intervalSeconds * 1000
if (totalPages == 0) return
isSlideshowRunning = true
btnToggleSlideshow.text = getString(R.string.stop_slideshow)
etInterval.isEnabled = false
btnOpenPdf.isEnabled = false
slideshowRunnable = object : Runnable {
override fun run() {
if (!isSlideshowRunning) return
currentPageIndex++
if (currentPageIndex >= totalPages) {
currentPageIndex = 0 // Loop back to start
}
showPage(currentPageIndex)
handler.postDelayed(this, intervalMillis)
}
}
handler.postDelayed(slideshowRunnable!!, intervalMillis)
}
private fun stopSlideshow() {
isSlideshowRunning = false
btnToggleSlideshow.text = getString(R.string.start_slideshow)
etInterval.isEnabled = true
btnOpenPdf.isEnabled = true
slideshowRunnable?.let { handler.removeCallbacks(it) }
}
private fun closePdf() {
try {
pdfRenderer?.close()
currentPdfDescriptor?.close()
} catch (e: IOException) {
e.printStackTrace()
}
pdfRenderer = null
currentPdfDescriptor = null
totalPages = 0
btnToggleSlideshow.isEnabled = false
}
override fun onDestroy() {
super.onDestroy()
closePdf()
}
}
Antigravityで質問して、エラーが出たらエラーメッセージをAntigravityに貼り付けて修正をお願いして・・とか面倒だなぁ・・と思ってたんですが。なんともタイムリーなことに新しくAndroidStudioにもGeminiを使ったAIサポート機能がついてたという!
Studioの全体映像はこういう感じ。これは・・アンドロイドアプリが作りたい場合はAntigravityを使う必要は全く無いな。もしもこれで一発で動くアンドロイドアプリなんかが出来たりしてしまった日には・・グーグルプレイ(アプリストア)にAIで作られた大量のアプリが溢れかえることになりそう・・・ま、でもそうそう一発で動くなんて事は・・
動いたーっ!UIは説明不要のシンプルさ。数字の部分はインターバルの秒数でOpenPDFボタンで端末内のPDFファイルを選択、Start SlideShowボタンでスライドショー開始・・・