Add Reading tab with Jhanas Guide and Retreat Instructions PDFs
Some checks failed
Build & Release APK / build (push) Has been cancelled

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 01:23:16 -07:00
parent 5519f19b34
commit cc800e756b
6 changed files with 82 additions and 0 deletions

View File

@@ -51,6 +51,7 @@ android {
dependencies { dependencies {
val composeBom = platform("androidx.compose:compose-bom:2024.10.01") val composeBom = platform("androidx.compose:compose-bom:2024.10.01")
implementation(composeBom) implementation(composeBom)
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material3:material3") implementation("androidx.compose.material3:material3")
implementation("androidx.activity:activity-compose:1.9.3") implementation("androidx.activity:activity-compose:1.9.3")

View File

@@ -27,6 +27,16 @@
android:name=".TimerService" android:name=".TimerService"
android:exported="false" android:exported="false"
android:foregroundServiceType="mediaPlayback" /> android:foregroundServiceType="mediaPlayback" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.marcus.meditationtimer.files"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application> </application>
</manifest> </manifest>

Binary file not shown.

Binary file not shown.

View File

@@ -1,10 +1,14 @@
package com.marcus.meditationtimer package com.marcus.meditationtimer
import android.Manifest import android.Manifest
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.widget.Toast
import androidx.core.content.FileProvider
import java.io.File
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
@@ -136,6 +140,7 @@ private fun App() {
) { ) {
Tab(selected = tab == 0, onClick = { tab = 0 }, text = { Text("Timer") }) Tab(selected = tab == 0, onClick = { tab = 0 }, text = { Text("Timer") })
Tab(selected = tab == 1, onClick = { tab = 1 }, text = { Text("History") }) Tab(selected = tab == 1, onClick = { tab = 1 }, text = { Text("History") })
Tab(selected = tab == 2, onClick = { tab = 2 }, text = { Text("Reading") })
} }
when (tab) { when (tab) {
0 -> TimerTab() 0 -> TimerTab()
@@ -143,6 +148,7 @@ private fun App() {
onEditSession = { ratingFor = it }, onEditSession = { ratingFor = it },
onOpenToken = { showTokenDialog = true }, onOpenToken = { showTokenDialog = true },
) )
2 -> ReadingTab()
} }
} }
@@ -430,6 +436,67 @@ private fun SessionRow(s: Session, onClick: () -> Unit) {
} }
} }
// -------------------------------------------------------------- Reading tab
private data class Doc(val title: String, val subtitle: String, val asset: String)
private val DOCS = listOf(
Doc("Pragmatic Jhanas Guide", "v4.3", "jhanas-guide.pdf"),
Doc("Retreat Instructions", "v6.0.3 · Day 1 & 2", "retreat-instructions.pdf"),
)
@Composable
private fun ReadingTab() {
val context = LocalContext.current
Column(
Modifier
.fillMaxSize()
.padding(20.dp)
) {
DOCS.forEach { doc ->
Column(
Modifier
.fillMaxWidth()
.clickable { openPdf(context, doc) }
.background(Color.White.copy(alpha = 0.05f), RoundedCornerShape(12.dp))
.padding(20.dp)
) {
Text(doc.title, color = Color.White.copy(alpha = 0.9f), fontSize = 16.sp)
Spacer(Modifier.height(4.dp))
Text(doc.subtitle, color = Dim, fontSize = 12.sp)
}
Spacer(Modifier.height(12.dp))
}
Text(
"Opens in the system PDF viewer.",
color = Dim,
fontSize = 11.sp,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth(),
)
}
}
private fun openPdf(context: Context, doc: Doc) {
try {
val dir = File(context.cacheDir, "reading").apply { mkdirs() }
val outFile = File(dir, doc.asset)
if (!outFile.exists() || outFile.length() == 0L) {
context.assets.open("reading/${doc.asset}").use { input ->
outFile.outputStream().use { input.copyTo(it) }
}
}
val uri = FileProvider.getUriForFile(context, "${context.packageName}.files", outFile)
context.startActivity(
Intent(Intent.ACTION_VIEW)
.setDataAndType(uri, "application/pdf")
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
)
} catch (e: Exception) {
Toast.makeText(context, "Could not open PDF: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
// ----------------------------------------------------------------- Dialogs // ----------------------------------------------------------------- Dialogs
@Composable @Composable

View File

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="reading" path="reading/" />
</paths>