v1.16: simplify usage alerts to fixed 90% and 100% (less aggressive)
Build APK / build (push) Successful in 1m50s

Replace the configurable threshold sliders with two fixed alert levels —
90% and 100% — per metric. Anti-spam now uses hysteresis instead of the
API reset-epoch (which could drift and re-fire): each level fires once
when crossed and re-arms only after usage drops back below it. Alerts are
posted only by the background worker, never the in-app refresh loop, so
you're not pinged while looking at the app. UI drops the sliders for a
one-line description; settings keep just the on/off switch.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 15:43:14 +00:00
parent 1d4356c1d7
commit a43fa5be92
5 changed files with 64 additions and 125 deletions
@@ -103,43 +103,15 @@ class MainActivity : AppCompatActivity() {
private fun setupNotificationSettings() {
binding.switchNotify.isChecked = prefs.isNotifyEnabled()
binding.sliderSession.value = prefs.getSessionThreshold().toFloat().coerceIn(50f, 100f)
binding.sliderWeekly.value = prefs.getWeeklyThreshold().toFloat().coerceIn(50f, 100f)
applyThresholdLabels()
applyNotifyControlsEnabled(prefs.isNotifyEnabled())
binding.switchNotify.setOnCheckedChangeListener { _, checked ->
prefs.setNotifyEnabled(checked)
applyNotifyControlsEnabled(checked)
if (checked) requestNotificationPermission()
}
binding.sliderSession.addOnChangeListener { _, value, _ ->
prefs.setSessionThreshold(value.toInt())
applyThresholdLabels()
}
binding.sliderWeekly.addOnChangeListener { _, value, _ ->
prefs.setWeeklyThreshold(value.toInt())
applyThresholdLabels()
}
// Alerts default on, so prompt for the runtime permission once on first launch
// (a user who never toggles the switch would otherwise never be asked).
if (prefs.isNotifyEnabled()) requestNotificationPermission()
}
private fun applyThresholdLabels() {
binding.tvSessionThreshLabel.text = "Session alert at ${prefs.getSessionThreshold()}%"
binding.tvWeeklyThreshLabel.text = "Weekly alert at ${prefs.getWeeklyThreshold()}%"
}
private fun applyNotifyControlsEnabled(enabled: Boolean) {
binding.sliderSession.isEnabled = enabled
binding.sliderWeekly.isEnabled = enabled
val alpha = if (enabled) 1f else 0.4f
binding.tvSessionThreshLabel.alpha = alpha
binding.tvWeeklyThreshLabel.alpha = alpha
}
private fun requestNotificationPermission() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return
if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
@@ -169,7 +141,8 @@ class MainActivity : AppCompatActivity() {
val merged = fresh.mergedWith(prefs.getUsageData())
prefs.saveUsageData(merged)
prefs.recordHistory(fresh)
Notifier.checkAndNotify(this, prefs, fresh)
// Note: alerts fire only from the background worker, not here — no point pinging you
// with a notification while you're already looking at the app.
updateUI(merged)
ClaudeUsageWidget.notifyDataChanged(this) // opening the app refreshes the widget too
if (binding.tvDebugInfo.visibility == View.VISIBLE) {