diff --git a/app/src/main/java/com/bm/andpictures/FileBrowserActivity.kt b/app/src/main/java/com/bm/andpictures/FileBrowserActivity.kt index 60f25cb..f740745 100644 --- a/app/src/main/java/com/bm/andpictures/FileBrowserActivity.kt +++ b/app/src/main/java/com/bm/andpictures/FileBrowserActivity.kt @@ -142,11 +142,26 @@ class FileBrowserActivity : AppCompatActivity() { currentDir = dirKey adapter.submit(entries) supportActionBar?.title = src.displayName(dirKey) - supportActionBar?.subtitle = "${src.title} · ${entries.count { !it.isDirectory }} items" + supportActionBar?.subtitle = browseSubtitle(src.title, entries) if (entries.isEmpty()) showMessage(getString(R.string.browse_empty)) else showGrid() } } + /** e.g. "Phone storage · 4 folders · 1 file" — folders and files counted separately. */ + private fun browseSubtitle(sourceTitle: String, entries: List): String { + val folders = entries.count { it.isDirectory } + val files = entries.size - folders + val parts = buildList { + if (folders > 0) add("$folders ${if (folders == 1) "folder" else "folders"}") + if (files > 0) add("$files ${if (files == 1) "file" else "files"}") + } + return if (parts.isEmpty()) { + "$sourceTitle · empty" + } else { + (listOf(sourceTitle) + parts).joinToString(" · ") + } + } + private fun navigateUp() { val src = source val cur = currentDir diff --git a/app/src/main/java/com/bm/andpictures/MainActivity.kt b/app/src/main/java/com/bm/andpictures/MainActivity.kt index 3e59639..a4745e9 100644 --- a/app/src/main/java/com/bm/andpictures/MainActivity.kt +++ b/app/src/main/java/com/bm/andpictures/MainActivity.kt @@ -36,6 +36,12 @@ class MainActivity : AppCompatActivity() { private lateinit var etDecadeFormat: EditText private lateinit var etMonthFormat: EditText + /** True while a manual/one-shot sync is queued or running. */ + private var oneShotSyncActive = false + + /** True only while the periodic safety-net worker is actually running. */ + private var periodicSyncRunning = false + private val mediaPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { } @@ -212,6 +218,15 @@ class MainActivity : AppCompatActivity() { statusText.text = p.lastStatus.ifBlank { getString(R.string.status_idle) } } + /** Repaints the status line from whatever sync is in flight right now. */ + private fun renderStatus() { + statusText.text = if (oneShotSyncActive || periodicSyncRunning) { + "Syncing…" + } else { + Prefs(this).lastStatus.ifBlank { getString(R.string.status_idle) } + } + } + private fun requestMediaPermission() { if (!PhotoStore.hasPermission(this)) { mediaPermissionLauncher.launch(PhotoStore.mediaPermission) @@ -241,15 +256,20 @@ class MainActivity : AppCompatActivity() { private fun observeWork() { val wm = WorkManager.getInstance(this) - fun applyState(infos: List) { - val latest = infos.lastOrNull() ?: return - when (latest.state) { - WorkInfo.State.ENQUEUED, WorkInfo.State.RUNNING -> - statusText.text = "Syncing…" - else -> refreshStatus() - } + + wm.getWorkInfosForUniqueWorkLiveData(Prefs.WORK_ONESHOT).observe(this) { infos -> + // ENQUEUED means "queued, waiting for a network/slot", which is still + // an active sync from the user's point of view. + val state = infos.lastOrNull()?.state + oneShotSyncActive = + state == WorkInfo.State.ENQUEUED || state == WorkInfo.State.RUNNING + renderStatus() + } + wm.getWorkInfosForUniqueWorkLiveData(Prefs.WORK_PERIODIC).observe(this) { infos -> + // The periodic safety-net job sits in ENQUEUED while it waits for its + // next window, so only a RUNNING job counts as a live sync. + periodicSyncRunning = infos.lastOrNull()?.state == WorkInfo.State.RUNNING + renderStatus() } - wm.getWorkInfosForUniqueWorkLiveData(Prefs.WORK_ONESHOT).observe(this) { applyState(it) } - wm.getWorkInfosForUniqueWorkLiveData(Prefs.WORK_PERIODIC).observe(this) { applyState(it) } } }