Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b36a9c636b | |||
| d9b886cdc6 | |||
| cc800e756b |
@@ -51,6 +51,7 @@ android {
|
||||
dependencies {
|
||||
val composeBom = platform("androidx.compose:compose-bom:2024.10.01")
|
||||
implementation(composeBom)
|
||||
implementation("androidx.core:core-ktx:1.13.1")
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
implementation("androidx.activity:activity-compose:1.9.3")
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
<application
|
||||
android:label="Meditation"
|
||||
android:icon="@drawable/ic_gong"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:theme="@android:style/Theme.Material.NoActionBar"
|
||||
android:allowBackup="true">
|
||||
|
||||
@@ -27,6 +27,16 @@
|
||||
android:name=".TimerService"
|
||||
android:exported="false"
|
||||
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>
|
||||
|
||||
</manifest>
|
||||
|
||||
BIN
app/src/main/assets/reading/jhanas-guide.pdf
Normal file
BIN
app/src/main/assets/reading/jhanas-guide.pdf
Normal file
Binary file not shown.
BIN
app/src/main/assets/reading/retreat-instructions.pdf
Normal file
BIN
app/src/main/assets/reading/retreat-instructions.pdf
Normal file
Binary file not shown.
@@ -1,10 +1,14 @@
|
||||
package com.marcus.meditationtimer
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.FileProvider
|
||||
import java.io.File
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
@@ -22,8 +26,10 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
@@ -128,6 +134,10 @@ private fun App() {
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(DeepBlue)
|
||||
// app targets SDK 35 → edge-to-edge is forced; without these the tab
|
||||
// row sits under the status bar and taps never reach it
|
||||
.statusBarsPadding()
|
||||
.navigationBarsPadding()
|
||||
) {
|
||||
TabRow(
|
||||
selectedTabIndex = tab,
|
||||
@@ -136,6 +146,7 @@ private fun App() {
|
||||
) {
|
||||
Tab(selected = tab == 0, onClick = { tab = 0 }, text = { Text("Timer") })
|
||||
Tab(selected = tab == 1, onClick = { tab = 1 }, text = { Text("History") })
|
||||
Tab(selected = tab == 2, onClick = { tab = 2 }, text = { Text("Reading") })
|
||||
}
|
||||
when (tab) {
|
||||
0 -> TimerTab()
|
||||
@@ -143,6 +154,7 @@ private fun App() {
|
||||
onEditSession = { ratingFor = it },
|
||||
onOpenToken = { showTokenDialog = true },
|
||||
)
|
||||
2 -> ReadingTab()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,6 +442,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
|
||||
|
||||
@Composable
|
||||
|
||||
29
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
29
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path android:pathData="M0,0h108v108H0z">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:startX="54" android:startY="0"
|
||||
android:endX="54" android:endY="108"
|
||||
android:startColor="#1D3550"
|
||||
android:endColor="#0A1622" />
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<!-- faint aura rings behind the gong -->
|
||||
<path
|
||||
android:pathData="M54,54m-44,0a44,44 0,1 1,88 0a44,44 0,1 1,-88 0"
|
||||
android:strokeColor="#22E3BC4B"
|
||||
android:strokeWidth="1.5"
|
||||
android:fillColor="#00000000" />
|
||||
<path
|
||||
android:pathData="M54,54m-38,0a38,38 0,1 1,76 0a38,38 0,1 1,-76 0"
|
||||
android:strokeColor="#14E3BC4B"
|
||||
android:strokeWidth="1"
|
||||
android:fillColor="#00000000" />
|
||||
</vector>
|
||||
51
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
51
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<!-- gong rim -->
|
||||
<path android:pathData="M54,54m-29,0a29,29 0,1 1,58 0a29,29 0,1 1,-58 0">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:startX="36" android:startY="30"
|
||||
android:endX="72" android:endY="80"
|
||||
android:startColor="#D9AE35"
|
||||
android:endColor="#A87E1C" />
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<!-- face -->
|
||||
<path android:pathData="M54,54m-24.5,0a24.5,24.5 0,1 1,49 0a24.5,24.5 0,1 1,-49 0">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:startX="38" android:startY="32"
|
||||
android:endX="70" android:endY="78"
|
||||
android:startColor="#F0CF66"
|
||||
android:endColor="#CD9F2E" />
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<!-- hammered ring -->
|
||||
<path
|
||||
android:pathData="M54,54m-17.5,0a17.5,17.5 0,1 1,35 0a17.5,17.5 0,1 1,-35 0"
|
||||
android:strokeColor="#B8892288"
|
||||
android:strokeWidth="1.2"
|
||||
android:fillColor="#00000000" />
|
||||
<!-- boss (center dome) -->
|
||||
<path android:pathData="M54,54m-9.5,0a9.5,9.5 0,1 1,19 0a9.5,9.5 0,1 1,-19 0">
|
||||
<aapt:attr name="android:fillColor">
|
||||
<gradient
|
||||
android:type="linear"
|
||||
android:startX="47" android:startY="46"
|
||||
android:endX="60" android:endY="62"
|
||||
android:startColor="#FCEBA8"
|
||||
android:endColor="#E3BC4B" />
|
||||
</aapt:attr>
|
||||
</path>
|
||||
<!-- specular highlight, upper-left crescent -->
|
||||
<path
|
||||
android:pathData="M38.5,40.5 a21,21 0 0 1 13,-7.5 a24,24 0 0 0 -16.5,10.8 z"
|
||||
android:fillColor="#66FFF7D6" />
|
||||
</vector>
|
||||
15
app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
15
app/src/main/res/drawable/ic_launcher_monochrome.xml
Normal file
@@ -0,0 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="108"
|
||||
android:viewportHeight="108">
|
||||
<path
|
||||
android:pathData="M54,54m-27,0a27,27 0,1 1,54 0a27,27 0,1 1,-54 0"
|
||||
android:strokeColor="#FFFFFF"
|
||||
android:strokeWidth="5"
|
||||
android:fillColor="#00000000" />
|
||||
<path
|
||||
android:pathData="M54,54m-9,0a9,9 0,1 1,18 0a9,9 0,1 1,-18 0"
|
||||
android:fillColor="#FFFFFF" />
|
||||
</vector>
|
||||
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
6
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
|
||||
</adaptive-icon>
|
||||
4
app/src/main/res/xml/file_paths.xml
Normal file
4
app/src/main/res/xml/file_paths.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<cache-path name="reading" path="reading/" />
|
||||
</paths>
|
||||
Reference in New Issue
Block a user