v1.14: usage history chart + threshold notifications

Add an in-app 7-day history chart and opt-in usage alerts, the two
features requested from the macOS Claude-Usage-Tracker that map cleanly
to an Android widget app.

History:
- UsageSnapshot model; PreferencesManager records session/weekly
  utilization on every refresh (7-day retention, <=600 points, collapses
  readings under 2 min apart to avoid worker+manual double-logging).
- HistoryChartView: dependency-free Canvas line chart (session/weekly,
  0/50/100% gridlines), breaks the line across >35-min gaps.
- New HISTORY card with chart + legend.

Notifications:
- Notifier posts when session/weekly crosses a user threshold, at most
  once per limit window (keyed on reset-epoch, re-arms on rollover).
- USAGE ALERTS card: enable switch + session/weekly sliders (50-100%,
  defaults 90/85). POST_NOTIFICATIONS permission + runtime request.
- Wired into the existing 5-min background worker.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 11:49:47 +00:00
parent ae0f466f50
commit 0520f0dc5e
10 changed files with 523 additions and 2 deletions
+131
View File
@@ -229,6 +229,137 @@
</LinearLayout>
<!-- History card -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/widget_background"
android:padding="20dp"
android:layout_marginTop="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="HISTORY"
android:textColor="#888888"
android:textSize="11sp"
android:letterSpacing="0.1" />
<me.khodak.claudeusage.HistoryChartView
android:id="@+id/historyChart"
android:layout_width="match_parent"
android:layout_height="140dp"
android:layout_marginTop="12dp" />
<!-- Legend -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="10dp"
android:gravity="center_vertical">
<View
android:layout_width="12dp"
android:layout_height="3dp"
android:background="#CC785C" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:layout_marginEnd="16dp"
android:text="Session"
android:textColor="#AAAAAA"
android:textSize="12sp" />
<View
android:layout_width="12dp"
android:layout_height="3dp"
android:background="#7B8FCC" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text="Weekly"
android:textColor="#AAAAAA"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
<!-- Notifications card -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/widget_background"
android:padding="20dp"
android:layout_marginTop="16dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="USAGE ALERTS"
android:textColor="#888888"
android:textSize="11sp"
android:letterSpacing="0.1" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/switchNotify"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:id="@+id/tvSessionThreshLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="14dp"
android:text="Session alert at 90%"
android:textColor="#FFFFFF"
android:textSize="14sp" />
<com.google.android.material.slider.Slider
android:id="@+id/sliderSession"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:valueFrom="50"
android:valueTo="100"
android:stepSize="5"
android:value="90" />
<TextView
android:id="@+id/tvWeeklyThreshLabel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:text="Weekly alert at 85%"
android:textColor="#FFFFFF"
android:textSize="14sp" />
<com.google.android.material.slider.Slider
android:id="@+id/sliderWeekly"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:valueFrom="50"
android:valueTo="100"
android:stepSize="5"
android:value="85" />
</LinearLayout>
<Button
android:id="@+id/btnRefresh"
android:layout_width="match_parent"