Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 688147b8ef | |||
| b36a9c636b |
@@ -9,7 +9,7 @@
|
|||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="Meditation"
|
android:label="Meditation"
|
||||||
android:icon="@drawable/ic_gong"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:theme="@android:style/Theme.Material.NoActionBar"
|
android:theme="@android:style/Theme.Material.NoActionBar"
|
||||||
android:allowBackup="true">
|
android:allowBackup="true">
|
||||||
|
|
||||||
|
|||||||
@@ -342,6 +342,9 @@ private fun HistoryTab(onEditSession: (Session) -> Unit, onOpenToken: () -> Unit
|
|||||||
val sessions by SessionStore.sessions.collectAsState()
|
val sessions by SessionStore.sessions.collectAsState()
|
||||||
val pending by SessionStore.pendingSync.collectAsState()
|
val pending by SessionStore.pendingSync.collectAsState()
|
||||||
|
|
||||||
|
// Pull the server history (incl. legacy Jhana-app sessions) on open.
|
||||||
|
LaunchedEffect(Unit) { SessionStore.refresh() }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ object SessionStore {
|
|||||||
publish(load())
|
publish(load())
|
||||||
}
|
}
|
||||||
syncPending()
|
syncPending()
|
||||||
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
var token: String
|
var token: String
|
||||||
@@ -93,6 +94,68 @@ object SessionStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pull the full server history (incl. sessions from other apps/devices,
|
||||||
|
* e.g. the legacy Jhana app) and merge it into the local log. Local
|
||||||
|
* sessions still awaiting push (synced=false) always win over the server
|
||||||
|
* copy; everything else takes the server's version.
|
||||||
|
*/
|
||||||
|
fun refresh() {
|
||||||
|
val tok = token
|
||||||
|
if (tok.isEmpty()) return
|
||||||
|
scope.launch {
|
||||||
|
val remote = fetchSessions(tok) ?: return@launch
|
||||||
|
synchronized(lock) {
|
||||||
|
val local = load().associateBy { it.id }
|
||||||
|
val merged = LinkedHashMap(local)
|
||||||
|
for (r in remote) {
|
||||||
|
val l = local[r.id]
|
||||||
|
if (l == null || l.synced) {
|
||||||
|
// Keep any locally-entered rating/note if the server has none.
|
||||||
|
merged[r.id] = r.copy(
|
||||||
|
rating = r.rating ?: l?.rating,
|
||||||
|
note = r.note ?: l?.note,
|
||||||
|
synced = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
persist(merged.values.toList())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchSessions(tok: String): List<Session>? = try {
|
||||||
|
val conn = URL("$BASE_URL/v1/sessions?limit=1000").openConnection() as HttpURLConnection
|
||||||
|
conn.requestMethod = "GET"
|
||||||
|
conn.setRequestProperty("Authorization", "Bearer $tok")
|
||||||
|
conn.connectTimeout = 10_000
|
||||||
|
conn.readTimeout = 10_000
|
||||||
|
val body = if (conn.responseCode in 200..299) {
|
||||||
|
conn.inputStream.bufferedReader().use { it.readText() }
|
||||||
|
} else null
|
||||||
|
conn.disconnect()
|
||||||
|
body?.let {
|
||||||
|
val arr = JSONObject(it).getJSONArray("sessions")
|
||||||
|
(0 until arr.length()).map { i ->
|
||||||
|
val o = arr.getJSONObject(i)
|
||||||
|
Session(
|
||||||
|
id = o.getString("id"),
|
||||||
|
startedAt = o.getString("startedAt"),
|
||||||
|
endedAt = o.optString("endedAt").ifEmpty { null },
|
||||||
|
plannedMin = o.optInt("plannedMin", 0),
|
||||||
|
actualMin = o.optInt("actualMin", 0),
|
||||||
|
completed = o.optBoolean("completed", false),
|
||||||
|
pauseCount = o.optInt("pauseCount", 0),
|
||||||
|
rating = if (o.isNull("rating")) null else o.optInt("rating"),
|
||||||
|
note = o.optString("note").ifEmpty { null },
|
||||||
|
synced = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
private fun postSession(s: Session, tok: String): Boolean = try {
|
private fun postSession(s: Session, tok: String): Boolean = try {
|
||||||
val conn = URL("$BASE_URL/v1/sessions").openConnection() as HttpURLConnection
|
val conn = URL("$BASE_URL/v1/sessions").openConnection() as HttpURLConnection
|
||||||
conn.requestMethod = "POST"
|
conn.requestMethod = "POST"
|
||||||
|
|||||||
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>
|
||||||
Reference in New Issue
Block a user