feat(widgets): M3 Expressive widget lineup — analog, digital, stopwatch, quick timer

Six widgets, each with minimal settings, styled with M3 Expressive shapes and
Material You colours, and sized proportionally to the placed widget
(SizeMode.Exact + LocalSize):

- Analog clock (new): nine faces on MaterialShapes dials (Round, Numeral,
  Day, Cookie, Sunny, Scallop, Clover, Square, Petal) with per-face hands,
  a seconds dot and Google-Clock-style orbiting date on some faces, and a
  day/date pill on others. Style picker previews inflate the real views.
- Digital clock: rebuilt as RemoteViews faces (Classic, Thin, Bold, Pill,
  Stacked) shared by the widget and its config previews; world clocks are
  opt-in and only on the card faces.
- Next alarm and Timers: shape badges, icon buttons, proportional readouts;
  a lone timer on a tall widget gets a stacked layout.
- Stopwatch (new): large readout with play/pause pill and lap/reset.
- Quick timer (new): a Scallop that is a timer of its own — tap to start a
  transient timer of its configured duration, tap again to reset/stop.

Colours resolve in the launcher (widget_* palette resources, GlanceTheme's
dynamic colours) so widgets follow day/night and the wallpaper. Data widgets
collect their flows inside the composition so a live Glance session never
shows stale state, chronometer bases use elapsedRealtime, and config
activities close cleanly on an invalid widget id.
This commit is contained in:
2026-10-02 18:01:11 +02:00
parent 5d03c654bf
commit 82fff19d3d
103 changed files with 3793 additions and 280 deletions
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Clover dial: `MaterialShapes.Clover4Leaf`'s cubics, normalized and laid
into this 100x100 viewport at 2..98 (generated once from the library's
own polygon, so it is the real M3 Expressive shape, not a lookalike).
Turned 45° so the lobes sit at 12/3/6/9 under the numerals, and scaled
to 0.88 so the turned lobes stay inside the viewport. Drawn by the dial
`ImageView` of `widget_analog_*.xml`; see `widget_analog_hour_hand.xml`
for the 480dp intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<group android:pivotX="50" android:pivotY="50" android:rotation="45" android:scaleX="0.88" android:scaleY="0.88">
<path
android:pathData="@string/widget_shape_path_clover"
android:fillColor="@color/widget_analog_dial_primary" />
</group>
</vector>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Cookie dial: `MaterialShapes.Cookie12Sided`'s cubics, normalized and laid into this
100x100 viewport at 2..98 (generated once from the library's own polygon,
so it is the real M3 Expressive shape, not a lookalike). Drawn by the
dial `ImageView` of `widget_analog_*.xml`; see
`widget_analog_hour_hand.xml` for the 480dp intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="@string/widget_shape_path_cookie"
android:fillColor="@color/widget_analog_dial_primary" />
</vector>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Invisible stand-in dial for every analog widget's `AnalogClock`. The
visible dial is drawn by an `ImageView` underneath, so numerals and badges
can sit between dial and hands. `AnalogClock` still needs a dial because it
derives the hands' scale from the dial's intrinsic size, so this matches
the hands' 480dp (see `widget_analog_hour_hand.xml`). -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M0,0 L100,0 L100,100 L0,100 Z"
android:fillColor="#00000000" />
</vector>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Petal dial: `MaterialShapes.Clover8Leaf`'s cubics, normalized and laid into this
100x100 viewport at 2..98 (generated once from the library's own polygon,
so it is the real M3 Expressive shape, not a lookalike). Drawn by the
dial `ImageView` of `widget_analog_*.xml`; see
`widget_analog_hour_hand.xml` for the 480dp intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="@string/widget_shape_path_petal"
android:fillColor="@color/widget_analog_dial_tertiary" />
</vector>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Round, Numeral and Day dial: `MaterialShapes.Circle`'s cubics, normalized and laid into this
100x100 viewport at 2..98 (generated once from the library's own polygon,
so it is the real M3 Expressive shape, not a lookalike). Drawn by the
dial `ImageView` of `widget_analog_*.xml`; see
`widget_analog_hour_hand.xml` for the 480dp intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M98.00,50.00 C98.00,54.97 97.22,59.93 95.67,64.71 C92.56,74.28 86.51,82.61 78.37,88.52 C70.24,94.43 60.44,97.61 50.39,97.61 C40.33,97.61 30.54,94.43 22.40,88.52 C14.27,82.61 8.21,74.28 5.11,64.71 C2.00,55.15 2.00,44.85 5.11,35.29 C8.21,25.72 14.27,17.39 22.40,11.48 C30.54,5.57 40.33,2.39 50.39,2.39 C60.44,2.39 70.24,5.57 78.37,11.48 C86.51,17.39 92.56,25.72 95.67,35.29 C97.22,40.07 98.00,45.03 98.00,50.00 Z"
android:fillColor="@color/widget_analog_dial_fill" />
</vector>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Scallop dial: `MaterialShapes.Cookie6Sided`'s cubics, normalized and laid into this
100x100 viewport at 2..98 (generated once from the library's own polygon,
so it is the real M3 Expressive shape, not a lookalike). Drawn by the
dial `ImageView` of `widget_analog_*.xml`; see
`widget_analog_hour_hand.xml` for the 480dp intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="@string/widget_shape_path_scallop"
android:fillColor="@color/widget_analog_dial_fill" />
</vector>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Square dial: `MaterialShapes.Square`'s cubics, normalized and laid into this
100x100 viewport at 2..98 (generated once from the library's own polygon,
so it is the real M3 Expressive shape, not a lookalike). Drawn by the
dial `ImageView` of `widget_analog_*.xml`; see
`widget_analog_hour_hand.xml` for the 480dp intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="@string/widget_shape_path_square"
android:fillColor="@color/widget_analog_dial_tertiary" />
</vector>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Sunny dial: `MaterialShapes.Sunny`'s cubics, normalized and laid into this
100x100 viewport at 2..98 (generated once from the library's own polygon,
so it is the real M3 Expressive shape, not a lookalike). Drawn by the
dial `ImageView` of `widget_analog_*.xml`; see
`widget_analog_hour_hand.xml` for the 480dp intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="@string/widget_shape_path_sunny"
android:fillColor="@color/widget_analog_dial_tertiary" />
</vector>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Bold hour hand: a wide capsule, same width as its minute hand, like
Google Clock's default face. See `widget_analog_hour_hand.xml` for the
pivot convention and the 480dp intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M45,36 A5,5 0 0,1 55,36 L55,50 A5,5 0 0,1 45,50 Z"
android:fillColor="@color/widget_analog_hand_hour" />
</vector>
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- `AnalogClock` centres a hand drawable's own bounds on the dial's centre
and rotates around that point — so the pivot is always (50,50) in this
100x100 viewport, and everything below it is the hand's short tail. The
platform's own default hand drawables render as oversized, mismatched
blobs on current devices, so every analog style sets its own via
`android:hand_hour`/`android:hand_minute` (confirmed working attrs; the
camelCase `hourHand`/`minuteHand` names do not exist).
480dp intrinsic size on purpose: `AnalogClock` only ever scales its
drawables *down* to fit the view, never up, so dial and hands must be at
least as large as the biggest widget for the clock to fill it. Dial and
hands share the same intrinsic size so they scale by the same factor. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M46.5,29.5 A3.5,3.5 0 0,1 53.5,29.5 L53.5,52 A3.5,3.5 0 0,1 46.5,52 Z"
android:fillColor="@color/widget_analog_hand_hour" />
</vector>
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Lollipop hour hand: a thin stick ending in a disc. See
`widget_analog_hour_hand.xml` for the pivot convention and the 480dp
intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M48,34 L52,34 L52,51 A2,2 0 0,1 48,51 Z"
android:fillColor="@color/widget_analog_hand_hour" />
<path
android:pathData="M50,33m-5,0a5,5 0 1,0 10,0a5,5 0 1,0 -10,0"
android:fillColor="@color/widget_analog_hand_hour" />
</vector>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Slim hour hand: a narrow capsule, fully rounded at both ends. See
`widget_analog_hour_hand.xml` for the pivot convention and the 480dp
intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M47.75,30 A2.25,2.25 0 0,1 52.25,30 L52.25,50 A2.25,2.25 0 0,1 47.75,50 Z"
android:fillColor="@color/widget_analog_hand_hour" />
</vector>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Bold minute hand: a wide capsule. See `widget_analog_hour_hand.xml` for
the pivot convention and the 480dp intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M45,21 A5,5 0 0,1 55,21 L55,50 A5,5 0 0,1 45,50 Z"
android:fillColor="@color/widget_analog_hand_minute" />
</vector>
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- See `widget_analog_hour_hand.xml` for the pivot convention and the 480dp
intrinsic size. Longer and narrower than the hour hand, and its own
colour, same as a real clock's two hands reading differently at a
glance. The centre dot is drawn here so it sits on top of the hour hand. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M47.5,15.5 A2.5,2.5 0 0,1 52.5,15.5 L52.5,52 A2.5,2.5 0 0,1 47.5,52 Z"
android:fillColor="@color/widget_analog_hand_minute" />
<path
android:pathData="M50,50m-3.5,0a3.5,3.5 0 1,0 7,0a3.5,3.5 0 1,0 -7,0"
android:fillColor="@color/widget_analog_hand_minute" />
</vector>
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Lollipop minute hand: a longer, thinner stick ending in a smaller disc,
plus the centre cap. See `widget_analog_hour_hand.xml` for the pivot
convention and the 480dp intrinsic size. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M48.6,18 L51.4,18 L51.4,51 A1.4,1.4 0 0,1 48.6,51 Z"
android:fillColor="@color/widget_analog_hand_minute" />
<path
android:pathData="M50,17m-4,0a4,4 0 1,0 8,0a4,4 0 1,0 -8,0"
android:fillColor="@color/widget_analog_hand_minute" />
<path
android:pathData="M50,50m-3.5,0a3.5,3.5 0 1,0 7,0a3.5,3.5 0 1,0 -7,0"
android:fillColor="@color/widget_analog_hand_minute" />
</vector>
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Slim minute hand: a narrow capsule over a ringed centre cap — a
ring in the minute colour around a dot in the hour colour. See
`widget_analog_hour_hand.xml` for the pivot convention and the 480dp
intrinsic size. Its tip stops at radius 30: Clover's notches reach in to
34, so a longer hand would poke out of that dial at :08, :23, :38, :53. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M48.5,21.5 A1.5,1.5 0 0,1 51.5,21.5 L51.5,50 A1.5,1.5 0 0,1 48.5,50 Z"
android:fillColor="@color/widget_analog_hand_minute" />
<path
android:pathData="M50,50m-4,0a4,4 0 1,0 8,0a4,4 0 1,0 -8,0"
android:fillColor="@color/widget_analog_hand_minute" />
<path
android:pathData="M50,50m-1.8,0a1.8,1.8 0 1,0 3.6,0a1.8,1.8 0 1,0 -3.6,0"
android:fillColor="@color/widget_analog_hand_hour" />
</vector>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Seconds hand as Google Clock draws it: just a dot orbiting inside the
rim (Sunny: radius 34, clear of its notches at 40). See
`widget_analog_hour_hand.xml` for the pivot convention and the 480dp
intrinsic size. Seconds hands exist on API 31+ only. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M50,16m-2.8,0a2.8,2.8 0 1,0 5.6,0a2.8,2.8 0 1,0 -5.6,0"
android:fillColor="@color/widget_analog_second" />
</vector>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- A heavier seconds dot near the rim (Square: radius 40, its edge is 48).
See `widget_analog_hour_hand.xml` for the pivot convention and the 480dp
intrinsic size. Seconds hands exist on API 31+ only. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M50,10m-4.2,0a4.2,4.2 0 1,0 8.4,0a4.2,4.2 0 1,0 -8.4,0"
android:fillColor="@color/widget_analog_second" />
</vector>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- The seconds dot of the dated faces, at radius 37 — inside Cookie's
notches (42). The date opposite it rides on a second,
invisible `AnalogClock`; see `widget_analog_plain.xml`. See
`widget_analog_hour_hand.xml` for the pivot convention and the 480dp
intrinsic size. Seconds hands exist on API 31+ only. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M50,13m-3.6,0a3.6,3.6 0 1,0 7.2,0a3.6,3.6 0 1,0 -7.2,0"
android:fillColor="@color/widget_analog_second" />
</vector>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Petal's seconds dot, at radius 32 — well inside Clover8Leaf's notches
(41), where the dated faces' radius-37 dot sat too close to the edge.
See `widget_analog_hour_hand.xml` for the pivot convention and the
480dp intrinsic size. Seconds hands exist on API 31+ only. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M50,18m-3.4,0a3.4,3.4 0 1,0 6.8,0a3.4,3.4 0 1,0 -6.8,0"
android:fillColor="@color/widget_analog_second" />
</vector>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Scallop's seconds dot: the dated faces' dot, moved in to radius 34 so
it keeps clear of Cookie6Sided's notches (41). The date opposite it is
unchanged; see `widget_analog_second_dot_dated.xml`. See
`widget_analog_hour_hand.xml` for the pivot convention and the 480dp
intrinsic size. Seconds hands exist on API 31+ only. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="M50,16m-3.6,0a3.6,3.6 0 1,0 7.2,0a3.6,3.6 0 1,0 -7.2,0"
android:fillColor="@color/widget_analog_second" />
</vector>
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- The Badge analog face's day-of-week pill — resource-qualified colour, not
`?android:attr/colorAccent`; see `values/colors.xml`'s comment. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/widget_analog_badge_pill" />
<corners android:radius="20dp" />
</shape>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- A widget's card. White: every user tints it with a `widget_*` palette
colour (`backgroundTint` in a layout, a Glance colour filter in code), so
one drawable serves every container role. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFFFFFFF" />
<corners android:radius="@dimen/widget_corner_radius" />
</shape>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- A circle, white so its user tints it — see `widget_bg_card.xml`. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="#FFFFFFFF" />
</shape>
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- A fully rounded pill, white so its user tints it — see `widget_bg_card.xml`. -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFFFFFFF" />
<corners android:radius="1000dp" />
</shape>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- The Bold digital face's card. Its colour is baked in rather than tinted: these
sit in plain `RemoteViews` layouts, where a runtime tint would resolve in
the app's process instead of the launcher's (`values/colors.xml`). -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/widget_primary_container" />
<corners android:radius="@dimen/widget_corner_radius" />
</shape>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- The Classic and Thin digital faces' card. Its colour is baked in rather than tinted: these
sit in plain `RemoteViews` layouts, where a runtime tint would resolve in
the app's process instead of the launcher's (`values/colors.xml`). -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/widget_surface" />
<corners android:radius="@dimen/widget_corner_radius" />
</shape>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- The Pill digital face's time pill. Its colour is baked in rather than tinted: these
sit in plain `RemoteViews` layouts, where a runtime tint would resolve in
the app's process instead of the launcher's (`values/colors.xml`). -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/widget_primary_container" />
<corners android:radius="1000dp" />
</shape>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- The Pill digital face's date pill. Its colour is baked in rather than tinted: these
sit in plain `RemoteViews` layouts, where a runtime tint would resolve in
the app's process instead of the launcher's (`values/colors.xml`). -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/widget_tertiary_container" />
<corners android:radius="1000dp" />
</shape>
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- The Stacked digital face's `MaterialShapes.Square`, colour baked in —
see `widget_digital_card_surface.xml`. 480dp like the analog dials, so
the fitCenter `ImageView` only ever scales it down. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="480dp"
android:height="480dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="@string/widget_shape_path_square"
android:fillColor="@color/widget_tertiary_container" />
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Material Symbols "add", white so its user tints it. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"
android:fillColor="#FFFFFFFF" />
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Material Symbols "alarm", white so its user tints it. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM7.88,3.39L6.6,1.86 2,5.71l1.29,1.53 4.59,-3.85zM12.5,8H11v6l4.75,2.85 0.75,-1.23 -4,-2.37V8zM12,4c-4.97,0 -9,4.03 -9,9s4.02,9 9,9c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,20c-3.87,0 -7,-3.13 -7,-7s3.13,-7 7,-7 7,3.13 7,7 -3.13,7 -7,7z"
android:fillColor="#FFFFFFFF" />
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Material Symbols "alarm_off", white so its user tints it. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,6c3.87,0 7,3.13 7,7 0,0.84 -0.16,1.65 -0.43,2.4l1.52,1.52c0.58,-1.19 0.91,-2.51 0.91,-3.92 0,-4.97 -4.03,-9 -9,-9 -1.41,0 -2.73,0.33 -3.92,0.91L9.6,6.43C10.35,6.16 11.16,6 12,6zM22,5.72l-4.6,-3.86 -1.29,1.53 4.6,3.86L22,5.72zM2.92,2.29L1.65,3.57 2.98,4.9l-1.11,0.93 1.42,1.42 1.11,-0.94 0.8,0.8C3.83,8.69 3,10.75 3,13c0,4.97 4.02,9 9,9 2.25,0 4.31,-0.83 5.89,-2.2l2.2,2.2 1.27,-1.27L3.89,3.27l-0.97,-0.98zM16.47,18.39C15.26,19.39 13.7,20 12,20c-3.87,0 -7,-3.13 -7,-7 0,-1.7 0.61,-3.26 1.61,-4.47l9.86,9.86zM8.02,3.28L6.6,1.86l-0.86,0.71 1.42,1.42 0.86,-0.71z"
android:fillColor="#FFFFFFFF" />
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Material Symbols "flag", white so its user tints it. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M14.4,6L14,4H5v17h2v-7h5.6l0.4,2h7V6z"
android:fillColor="#FFFFFFFF" />
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Material Symbols "pause", white so its user tints it. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,19h4V5H6v14zM14,5v14h4V5h-4z"
android:fillColor="#FFFFFFFF" />
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Material Symbols "play_arrow", white so its user tints it. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M8,5v14l11,-7z"
android:fillColor="#FFFFFFFF" />
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Material Symbols "replay", white so its user tints it. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6 -6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8 -3.58,-8 -8,-8z"
android:fillColor="#FFFFFFFF" />
</vector>
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Material Symbols "stop", white so its user tints it. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M6,6h12v12H6z"
android:fillColor="#FFFFFFFF" />
</vector>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- `MaterialShapes.Cookie12Sided` (`values/widget_shapes.xml`), white so its user
tints it — see `widget_bg_card.xml`. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="@string/widget_shape_path_cookie"
android:fillColor="#FFFFFFFF" />
</vector>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- `MaterialShapes.Cookie6Sided` (`values/widget_shapes.xml`), white so its user
tints it — see `widget_bg_card.xml`. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="@string/widget_shape_path_scallop"
android:fillColor="#FFFFFFFF" />
</vector>
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- `MaterialShapes.Square` (`values/widget_shapes.xml`), white so its user
tints it — see `widget_bg_card.xml`. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="100"
android:viewportHeight="100">
<path
android:pathData="@string/widget_shape_path_square"
android:fillColor="#FFFFFFFF" />
</vector>
@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Day face: the numeral face with a day-of-week pill in place of the 3 —
the same stack as `widget_analog_plain.xml`. The pill is a plain
`TextClock` holding only a day pattern, set by
`ui/widget/AnalogClockWidgetContent.kt` (which also sizes everything to
the widget), so it re-renders at midnight with no app-side ticking. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/widget_analog_dial"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:scaleType="fitCenter"
android:src="@drawable/widget_analog_dial_round" />
<TextView
android:id="@+id/widget_analog_numeral_12"
style="@style/WidgetAnalogNumeral"
android:layout_gravity="top|center_horizontal"
android:text="12" />
<TextView
android:id="@+id/widget_analog_numeral_6"
style="@style/WidgetAnalogNumeral"
android:layout_gravity="bottom|center_horizontal"
android:text="6" />
<TextView
android:id="@+id/widget_analog_numeral_9"
style="@style/WidgetAnalogNumeral"
android:layout_gravity="start|center_vertical"
android:text="9" />
<!-- Wrapper only so its end padding can inset the pill from the dial's
edge in proportion to the widget (`setViewPadding` works on every
API level, layout margins only from 31). -->
<FrameLayout
android:id="@+id/widget_analog_badge_frame"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextClock
android:id="@+id/widget_analog_badge_day"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|end"
android:background="@drawable/widget_badge_pill_background"
android:includeFontPadding="false"
android:textColor="@color/widget_analog_badge_pill_text"
android:textStyle="bold"
android:textSize="12sp" />
</FrameLayout>
<AnalogClock
android:id="@+id/widget_clock_analog"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dial="@drawable/widget_analog_dial_none"
android:hand_hour="@drawable/widget_analog_hour_hand"
android:hand_minute="@drawable/widget_analog_minute_hand" />
</FrameLayout>
@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Numeral face: the dial plus four oversized hour numerals inside it, under
the hands — the same stack as `widget_analog_plain.xml`. The numerals'
text size and edge inset are set from
`ui/widget/AnalogClockWidgetContent.kt` in proportion to the widget's
size; fixed sp/dp here would not scale with the dial. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/widget_analog_dial"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:scaleType="fitCenter"
android:src="@drawable/widget_analog_dial_round" />
<TextView
android:id="@+id/widget_analog_numeral_12"
style="@style/WidgetAnalogNumeral"
android:layout_gravity="top|center_horizontal"
android:text="12" />
<TextView
android:id="@+id/widget_analog_numeral_6"
style="@style/WidgetAnalogNumeral"
android:layout_gravity="bottom|center_horizontal"
android:text="6" />
<TextView
android:id="@+id/widget_analog_numeral_9"
style="@style/WidgetAnalogNumeral"
android:layout_gravity="start|center_vertical"
android:text="9" />
<TextView
android:id="@+id/widget_analog_numeral_3"
style="@style/WidgetAnalogNumeral"
android:layout_gravity="end|center_vertical"
android:text="3" />
<AnalogClock
android:id="@+id/widget_clock_analog"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dial="@drawable/widget_analog_dial_none"
android:hand_hour="@drawable/widget_analog_hour_hand"
android:hand_minute="@drawable/widget_analog_minute_hand" />
</FrameLayout>
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Plain analog face. Every analog layout is the same stack: the dial
`ImageView` (its shape drawable is set per style by
`ui/widget/AnalogClockWidgetContent.kt`, and fitCenter scales it up or
down to the widget), then any overlays, then an `AnalogClock` with an
invisible dial that only draws the hands. `AnalogClock` alone never
scales its drawables up — see `widget_analog_hour_hand.xml`. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/widget_analog_dial"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:scaleType="fitCenter"
android:src="@drawable/widget_analog_dial_round" />
<!-- The date that orbits opposite the seconds dot on dated faces. Its own
invisible clock — every drawable transparent except the seconds hand,
which is the date — because one hand can carry only one tint, and
the date (neutral) and the dot (accent) need two. Under the main
clock so the hands sweep over it; gone unless a face shows it. -->
<AnalogClock
android:id="@+id/widget_clock_analog_date"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:dial="@drawable/widget_analog_dial_none"
android:hand_hour="@drawable/widget_analog_dial_none"
android:hand_minute="@drawable/widget_analog_dial_none" />
<AnalogClock
android:id="@+id/widget_clock_analog"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dial="@drawable/widget_analog_dial_none"
android:hand_hour="@drawable/widget_analog_hour_hand"
android:hand_minute="@drawable/widget_analog_minute_hand" />
</FrameLayout>
@@ -1,13 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Embedded inside a Glance layout via AndroidRemoteViews (widget/TimerWidget.kt):
Glance has no chronometer composable, and RemoteViews.setChronometer +
setChronometerCountDown is exactly the mechanism TimerNotifications.kt
already uses for the same reason — the platform ticks it, not the app.
A running timer's or stopwatch's readout, embedded in their Glance layouts
via AndroidRemoteViews (ui/widget/WidgetKit.kt): Glance has no chronometer
composable, and RemoteViews.setChronometer is exactly the mechanism
TimerNotifications.kt already uses for the same reason — the platform
ticks it, not the app. Its text size is set to fit the widget at runtime.
-->
<Chronometer xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_chronometer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_surface"
android:textSize="28sp" />
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
The quick-timer widget's countdown (ui/widget/QuickTimerWidgetContent.kt):
`widget_chronometer.xml` in the colour that reads on its primary-container
shape.
-->
<Chronometer xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_chronometer_on_primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_primary_container"
android:textSize="28sp" />
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Bold digital face: a black-weight time on a primary-container card. Every digital face is one of these `RemoteViews` layouts,
filled in and sized to the widget by `ui/widget/DigitalFaces.kt` — the
same builder the config picker's previews inflate. The time and date are
`TextClock`s, so they tick in the launcher with no app-side updates. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_digital_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/widget_digital_card_primary"
android:gravity="center"
android:orientation="vertical">
<TextClock
android:id="@+id/widget_digital_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-black"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_primary_container"
android:textSize="48sp" />
<TextClock
android:id="@+id/widget_digital_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_primary_container"
android:textSize="14sp" />
<!-- World-clock rows (`widget_digital_city*.xml`), added at runtime. -->
<LinearLayout
android:id="@+id/widget_digital_cities"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone" />
</LinearLayout>
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- One world-clock row on the surface card: the city, then its own `TextClock`
(its zone set at runtime). Sized by `ui/widget/DigitalFaces.kt`. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/widget_digital_city_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_surface_variant"
android:textSize="14sp" />
<TextClock
android:id="@+id/widget_digital_city_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_surface"
android:textSize="14sp" />
</LinearLayout>
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- One world-clock row on the Bold face's primary card: the city, then its own `TextClock`
(its zone set at runtime). Sized by `ui/widget/DigitalFaces.kt`. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<TextView
android:id="@+id/widget_digital_city_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ellipsize="end"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_primary_container"
android:textSize="14sp" />
<TextClock
android:id="@+id/widget_digital_city_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_primary_container"
android:textSize="14sp" />
</LinearLayout>
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Classic digital face: medium time and an accent date on the shared surface card. Every digital face is one of these `RemoteViews` layouts,
filled in and sized to the widget by `ui/widget/DigitalFaces.kt` — the
same builder the config picker's previews inflate. The time and date are
`TextClock`s, so they tick in the launcher with no app-side updates. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_digital_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/widget_digital_card_surface"
android:gravity="center"
android:orientation="vertical">
<TextClock
android:id="@+id/widget_digital_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_surface"
android:textSize="48sp" />
<TextClock
android:id="@+id/widget_digital_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_primary"
android:textSize="14sp" />
<!-- World-clock rows (`widget_digital_city*.xml`), added at runtime. -->
<LinearLayout
android:id="@+id/widget_digital_cities"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone" />
</LinearLayout>
@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Pill digital face: the time in a primary-container pill and the date in
a smaller tertiary one under it, floating on the home screen with no card
— see `widget_digital_classic.xml` for how every digital face is built. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextClock
android:id="@+id/widget_digital_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_primary_container"
android:textSize="48sp"
android:background="@drawable/widget_digital_pill_primary"
android:textStyle="bold" />
<!-- Wrapper only so its top padding can space the pills in proportion to
the widget (`setViewPadding` works on every API level, layout
margins only from 31). -->
<FrameLayout
android:id="@+id/widget_digital_date_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextClock
android:id="@+id/widget_digital_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_tertiary_container"
android:textSize="14sp"
android:background="@drawable/widget_digital_pill_tertiary" />
</FrameLayout>
</LinearLayout>
@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Stacked digital face: hours over minutes, black weight, on an M3 Square
floating on the home screen like the analog dials — the shape is a
fitCenter `ImageView`, so it stays square whatever the widget's aspect.
See `widget_digital_classic.xml` for how every digital face is built. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no"
android:scaleType="fitCenter"
android:src="@drawable/widget_digital_shape_square" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextClock
android:id="@+id/widget_digital_hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-black"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_tertiary_container"
android:textSize="48sp" />
<TextClock
android:id="@+id/widget_digital_minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-black"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_tertiary"
android:textSize="48sp" />
<TextClock
android:id="@+id/widget_digital_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_tertiary_container"
android:textSize="14sp" />
</LinearLayout>
</FrameLayout>
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Thin digital face: a thin, accent-coloured time on the shared surface card. Every digital face is one of these `RemoteViews` layouts,
filled in and sized to the widget by `ui/widget/DigitalFaces.kt` — the
same builder the config picker's previews inflate. The time and date are
`TextClock`s, so they tick in the launcher with no app-side updates. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_digital_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/widget_digital_card_surface"
android:gravity="center"
android:orientation="vertical">
<TextClock
android:id="@+id/widget_digital_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-thin"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_primary"
android:textSize="48sp" />
<TextClock
android:id="@+id/widget_digital_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_surface_variant"
android:textSize="14sp" />
<!-- World-clock rows (`widget_digital_city*.xml`), added at runtime. -->
<LinearLayout
android:id="@+id/widget_digital_cities"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:visibility="gone" />
</LinearLayout>
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
A stopped timer's or stopwatch's readout: a plain TextView styled exactly
like `widget_chronometer.xml` (no font padding), so a readout measures the
same running or not and the layout around it doesn't jump or overflow
when it switches. Text, size and colour are set at runtime
(ui/widget/WidgetKit.kt).
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_readout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:includeFontPadding="false"
android:maxLines="1"
android:textColor="@color/widget_on_surface"
android:textSize="28sp" />
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Embedded inside a Glance layout via AndroidRemoteViews (widget/ClockWidget.kt):
a self-updating platform clock that honours the device's 12/24-hour
setting on its own, with no per-second app code. The timezone is set at
render time via RemoteViews.setString(id, "setTimeZone", zoneId).
-->
<TextClock xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/widget_text_clock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
android:textSize="28sp" />
@@ -0,0 +1,24 @@
<resources>
<!-- Dark-mode tones of `values-v31/colors.xml`'s Material You palette. -->
<color name="widget_analog_dial_fill">@android:color/system_accent2_800</color>
<color name="widget_analog_dial_primary">@android:color/system_accent1_800</color>
<color name="widget_analog_dial_tertiary">@android:color/system_accent3_800</color>
<color name="widget_analog_hand_hour">@android:color/system_accent1_100</color>
<color name="widget_analog_hand_minute">@android:color/system_accent1_300</color>
<color name="widget_analog_numeral">@android:color/system_accent1_200</color>
<color name="widget_analog_second">@android:color/system_accent3_300</color>
<color name="widget_analog_date">@android:color/system_neutral1_50</color>
<color name="widget_analog_badge_pill">@android:color/system_accent3_300</color>
<color name="widget_analog_badge_pill_text">@android:color/system_accent3_900</color>
<!-- Dark tones of the shared widget palette in `values-v31/colors.xml`. -->
<color name="widget_surface">@android:color/system_accent2_800</color>
<color name="widget_on_surface">@android:color/system_neutral1_50</color>
<color name="widget_on_surface_variant">@android:color/system_neutral2_200</color>
<color name="widget_primary">@android:color/system_accent1_200</color>
<color name="widget_primary_container">@android:color/system_accent1_700</color>
<color name="widget_on_primary_container">@android:color/system_accent1_100</color>
<color name="widget_tertiary">@android:color/system_accent3_200</color>
<color name="widget_tertiary_container">@android:color/system_accent3_700</color>
<color name="widget_on_tertiary_container">@android:color/system_accent3_100</color>
</resources>
+26
View File
@@ -0,0 +1,26 @@
<resources>
<!-- Dark-mode counterparts of the analog widget's colours — see the light
`values/colors.xml` for why these are literal, resource-qualified
colours rather than `?android:attr/...`. -->
<color name="widget_analog_dial_fill">#FF2B2F26</color>
<color name="widget_analog_dial_primary">#FF3E4C32</color>
<color name="widget_analog_dial_tertiary">#FF334B47</color>
<color name="widget_analog_hand_hour">#FFE4E9E0</color>
<color name="widget_analog_hand_minute">#FFB4C7A4</color>
<color name="widget_analog_numeral">#FFC9D9B9</color>
<color name="widget_analog_second">#FFA5CEC7</color>
<color name="widget_analog_date">#FFE3E3DC</color>
<color name="widget_analog_badge_pill">#FFB4C7A4</color>
<color name="widget_analog_badge_pill_text">#FF22261C</color>
<!-- Dark tones of the shared widget palette in `values/colors.xml`. -->
<color name="widget_surface">#FF2B2F26</color>
<color name="widget_on_surface">#FFE4E9E0</color>
<color name="widget_on_surface_variant">#FFC4C8BA</color>
<color name="widget_primary">#FFCEE2BA</color>
<color name="widget_primary_container">#FF4A583B</color>
<color name="widget_on_primary_container">#FFE8F6D9</color>
<color name="widget_tertiary">#FFA5CEC7</color>
<color name="widget_tertiary_container">#FF244D48</color>
<color name="widget_on_tertiary_container">#FFC1ECE4</color>
</resources>
+28
View File
@@ -0,0 +1,28 @@
<resources>
<!-- Analog widget on API 31+: Material You system palette, so the dial
follows the wallpaper like the launcher's own widgets. These
`@android:color/system_*` tones are resolved by the launcher, and
`values-night-v31/` swaps the tones for dark mode. -->
<color name="widget_analog_dial_fill">@android:color/system_accent2_50</color>
<color name="widget_analog_dial_primary">@android:color/system_accent1_100</color>
<color name="widget_analog_dial_tertiary">@android:color/system_accent3_100</color>
<color name="widget_analog_hand_hour">@android:color/system_accent1_700</color>
<color name="widget_analog_hand_minute">@android:color/system_accent1_400</color>
<color name="widget_analog_numeral">@android:color/system_accent1_600</color>
<color name="widget_analog_second">@android:color/system_accent3_500</color>
<color name="widget_analog_date">@android:color/system_neutral1_900</color>
<color name="widget_analog_badge_pill">@android:color/system_accent3_200</color>
<color name="widget_analog_badge_pill_text">@android:color/system_accent3_900</color>
<!-- The shared widget palette on API 31+: Material You system tones,
mapped onto the M3 roles as the platform's own dynamic scheme maps them. -->
<color name="widget_surface">@android:color/system_accent2_50</color>
<color name="widget_on_surface">@android:color/system_neutral1_900</color>
<color name="widget_on_surface_variant">@android:color/system_neutral2_700</color>
<color name="widget_primary">@android:color/system_accent1_600</color>
<color name="widget_primary_container">@android:color/system_accent1_100</color>
<color name="widget_on_primary_container">@android:color/system_accent1_900</color>
<color name="widget_tertiary">@android:color/system_accent3_600</color>
<color name="widget_tertiary_container">@android:color/system_accent3_100</color>
<color name="widget_on_tertiary_container">@android:color/system_accent3_900</color>
</resources>
+4
View File
@@ -0,0 +1,4 @@
<resources>
<!-- The launcher's own widget corner, so a card's drawn corners match its clip. -->
<dimen name="widget_corner_radius">@android:dimen/system_app_widget_background_radius</dimen>
</resources>
+33
View File
@@ -4,4 +4,37 @@
<color name="seed">#FF6B7A5C</color>
<!-- Adaptive icon background -->
<color name="ic_launcher_background">#FF6B7A5C</color>
<!-- Analog widget dial/hands/badge (light). `?android:attr/colorControlHighlight`
was tried first and dropped: it is a low-contrast pressed-state overlay
colour, not meant to stand alone as an opaque fill, and read as a
near-black smudge regardless of the launcher's day/night. These are
resolved against the host process's own configuration (`values-night/`
below), the same mechanism the platform itself uses for day/night —
not a `?android:attr`, which proved unreliable here. -->
<color name="widget_analog_dial_fill">#FFE8EAE1</color>
<color name="widget_analog_dial_primary">#FFD7E8C6</color>
<color name="widget_analog_dial_tertiary">#FFCCE8E3</color>
<color name="widget_analog_hand_hour">#FF44483E</color>
<color name="widget_analog_hand_minute">#FF6B7A5C</color>
<color name="widget_analog_numeral">#FF4E5C40</color>
<color name="widget_analog_second">#FF3D6660</color>
<color name="widget_analog_date">#FF1A1C18</color>
<color name="widget_analog_badge_pill">#FF6B7A5C</color>
<color name="widget_analog_badge_pill_text">#FFFFFFFF</color>
<!-- Every other widget's palette (light), named after the M3 roles they
stand in for. Like the analog colours above, resource-qualified
rather than `?android:attr/...` or a Glance theme, so the launcher
resolves them against its own day/night (`values-night/`), and on
API 31+ against the wallpaper (`values-v31/`). -->
<color name="widget_surface">#FFE8EAE1</color>
<color name="widget_on_surface">#FF1C1F1A</color>
<color name="widget_on_surface_variant">#FF44483E</color>
<color name="widget_primary">#FF5C6A4E</color>
<color name="widget_primary_container">#FFD7E8C6</color>
<color name="widget_on_primary_container">#FF1D2A10</color>
<color name="widget_tertiary">#FF3D6660</color>
<color name="widget_tertiary_container">#FFCCE8E3</color>
<color name="widget_on_tertiary_container">#FF05201C</color>
</resources>
+4
View File
@@ -0,0 +1,4 @@
<resources>
<!-- Widget card corners below API 31, where there is no system widget radius. -->
<dimen name="widget_corner_radius">20dp</dimen>
</resources>
+35 -1
View File
@@ -348,6 +348,40 @@
<string name="widget_next_alarm_today">Today</string>
<string name="widget_timer_description">Timer</string>
<string name="widget_timer_new">New timer</string>
<string name="widget_timer_expired">Time\'s up</string>
<string name="widget_timer_expired">Done</string>
<string name="widget_clock_description">Clock</string>
<!-- M11: digital clock widget faces + per-instance config -->
<string name="widget_config_title">Digital clock</string>
<string name="widget_config_style">Style</string>
<string name="clock_face_regular">Classic</string>
<string name="clock_face_bold">Bold</string>
<string name="clock_face_light">Thin</string>
<string name="clock_face_pill">Pill</string>
<string name="clock_face_stacked">Stacked</string>
<string name="widget_config_show_date">Show date</string>
<string name="widget_config_show_world_clocks">World clocks</string>
<string name="widget_config_show_world_clocks_hint">Up to three cities from your World clock tab</string>
<!-- M11: analog clock widget -->
<string name="widget_analog_description">Analog clock</string>
<string name="widget_analog_config_title">Analog clock</string>
<string name="analog_style_round">Round</string>
<string name="analog_style_numeral">Numeral</string>
<string name="analog_style_badge">Day</string>
<string name="analog_style_cookie">Cookie</string>
<string name="analog_style_clover">Clover</string>
<string name="analog_style_petal">Petal</string>
<string name="analog_style_sunny">Sunny</string>
<string name="analog_style_square">Square</string>
<string name="analog_style_scallop">Scallop</string>
<!-- M11: stopwatch widget -->
<string name="widget_stopwatch_description">Stopwatch</string>
<string name="widget_stopwatch_laps">Laps: %1$d</string>
<!-- M11: quick-timer widget -->
<string name="widget_quick_timer_description">Quick timer</string>
<string name="widget_quick_timer_config_title">Quick timer</string>
<string name="widget_quick_timer_start">Start %1$s</string>
</resources>
+12
View File
@@ -8,4 +8,16 @@
<!-- The exported AlarmClock door has no content view: translucent and
title-less so nothing flashes on the way through (M9 D3). -->
<style name="Theme.Clockula.Invisible" parent="android:Theme.Translucent.NoTitleBar" />
<!-- An hour numeral on the analog widget's dial (`layout/widget_analog_*.xml`).
Its text size and inset are overridden per widget size at runtime. -->
<style name="WidgetAnalogNumeral">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:includeFontPadding">false</item>
<item name="android:textColor">@color/widget_analog_numeral</item>
<item name="android:fontFamily">sans-serif-medium</item>
<item name="android:textStyle">bold</item>
<item name="android:textSize">18sp</item>
</style>
</resources>
+20
View File
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- M3 Expressive `MaterialShapes` outlines as vector path data in a 100x100
viewport at 2..98: each generated once from the library's own
`RoundedPolygon` (normalized cubics), so they are the real shapes, not
lookalikes. Shared by the analog dials and every other widget's shape
drawables (`drawable/widget_shape_*.xml`) through `android:pathData`. -->
<resources>
<!-- MaterialShapes.Cookie12Sided -->
<string name="widget_shape_path_cookie" translatable="false">M50.00,2.49 C52.46,2.49 54.92,3.48 56.72,5.46 C59.11,8.09 62.82,9.08 66.20,8.00 C71.30,6.38 76.70,9.49 77.84,14.72 C78.60,18.19 81.31,20.90 84.78,21.66 C90.01,22.81 93.13,28.20 91.50,33.30 C90.42,36.69 91.42,40.39 94.04,42.78 C98.00,46.39 98.00,52.62 94.04,56.23 C91.42,58.62 90.42,62.32 91.50,65.71 C93.13,70.81 90.01,76.20 84.78,77.35 C81.31,78.11 78.60,80.82 77.84,84.29 C76.70,89.52 71.30,92.63 66.20,91.01 C62.82,89.93 59.11,90.92 56.72,93.55 C53.11,97.51 46.89,97.51 43.28,93.55 C40.89,90.92 37.18,89.93 33.80,91.01 C28.70,92.63 23.30,89.52 22.16,84.29 C21.40,80.82 18.69,78.11 15.22,77.35 C9.99,76.20 6.87,70.81 8.50,65.71 C9.58,62.32 8.58,58.62 5.96,56.23 C2.00,52.62 2.00,46.39 5.96,42.78 C8.58,40.39 9.58,36.69 8.50,33.30 C6.87,28.20 9.99,22.81 15.22,21.66 C18.69,20.90 21.40,18.19 22.16,14.72 C23.30,9.49 28.70,6.38 33.80,8.00 C37.18,9.08 40.89,8.09 43.28,5.46 C45.08,3.48 47.54,2.49 50.00,2.49 Z</string>
<!-- MaterialShapes.Sunny -->
<string name="widget_shape_path_sunny" translatable="false">M97.70,50.00 C97.70,51.72 97.11,53.45 95.92,54.84 C93.49,57.69 91.06,60.54 88.63,63.39 C87.61,64.59 87.00,66.07 86.87,67.64 C86.58,71.37 86.28,75.10 85.98,78.84 C85.69,82.49 82.79,85.39 79.14,85.68 C75.40,85.98 71.67,86.28 67.94,86.58 C66.37,86.70 64.88,87.32 63.69,88.34 C60.84,90.76 57.99,93.19 55.14,95.62 C52.35,98.00 48.25,98.00 45.46,95.62 C42.61,93.19 39.76,90.76 36.91,88.34 C35.71,87.32 34.22,86.70 32.66,86.58 C28.93,86.28 25.19,85.98 21.46,85.68 C17.81,85.39 14.91,82.49 14.61,78.84 C14.32,75.10 14.02,71.37 13.72,67.64 C13.60,66.07 12.98,64.59 11.96,63.39 C9.53,60.54 7.10,57.69 4.67,54.84 C2.30,52.05 2.30,47.95 4.67,45.16 C7.10,42.31 9.53,39.46 11.96,36.61 C12.98,35.41 13.60,33.93 13.72,32.36 C14.02,28.63 14.32,24.90 14.61,21.16 C14.91,17.51 17.81,14.61 21.46,14.32 C25.19,14.02 28.93,13.72 32.66,13.42 C34.22,13.30 35.71,12.68 36.91,11.66 C39.76,9.24 42.61,6.81 45.46,4.38 C48.25,2.00 52.35,2.00 55.14,4.38 C57.99,6.81 60.84,9.24 63.69,11.66 C64.88,12.68 66.37,13.30 67.94,13.42 C71.67,13.72 75.40,14.02 79.14,14.32 C82.79,14.61 85.69,17.51 85.98,21.16 C86.28,24.90 86.58,28.63 86.87,32.36 C87.00,33.93 87.61,35.41 88.63,36.61 C91.06,39.46 93.49,42.31 95.92,45.16 C97.11,46.55 97.70,48.28 97.70,50.00 Z</string>
<!-- MaterialShapes.Cookie6Sided -->
<string name="widget_shape_path_scallop" translatable="false">M70.79,85.80 C69.17,86.73 67.66,87.86 66.30,89.17 C66.26,89.22 66.21,89.26 66.16,89.31 C57.15,98.00 42.87,97.99 33.87,89.28 C31.16,86.66 27.85,84.75 24.23,83.70 C24.17,83.69 24.10,83.67 24.04,83.65 C12.00,80.19 4.87,67.82 7.92,55.67 C8.83,52.01 8.83,48.19 7.93,44.53 C7.91,44.47 7.89,44.40 7.88,44.34 C4.86,32.19 12.01,19.83 24.05,16.39 C27.67,15.35 30.98,13.44 33.70,10.83 C33.74,10.78 33.79,10.74 33.84,10.69 C42.85,2.00 57.13,2.01 66.13,10.72 C68.84,13.34 72.15,15.25 75.77,16.30 C75.83,16.31 75.90,16.33 75.96,16.35 C88.00,19.81 95.13,32.18 92.08,44.33 C91.17,47.99 91.17,51.81 92.07,55.47 C92.09,55.53 92.11,55.60 92.12,55.66 C95.14,67.81 87.99,80.17 75.95,83.61 C74.14,84.13 72.41,84.86 70.79,85.80 Z</string>
<!-- MaterialShapes.Square -->
<string name="widget_shape_path_square" translatable="false">M89.56,89.56 C84.35,94.78 77.15,98.00 69.20,98.00 C56.40,98.00 43.60,98.00 30.80,98.00 C14.89,98.00 2.00,85.11 2.00,69.20 C2.00,56.40 2.00,43.60 2.00,30.80 C2.00,14.89 14.89,2.00 30.80,2.00 C43.60,2.00 56.40,2.00 69.20,2.00 C85.11,2.00 98.00,14.89 98.00,30.80 C98.00,43.60 98.00,56.40 98.00,69.20 C98.00,77.15 94.78,84.35 89.56,89.56 Z</string>
<!-- MaterialShapes.Clover8Leaf -->
<string name="widget_shape_path_petal" translatable="false">M50.00,8.84 C50.70,8.47 51.39,8.10 52.09,7.73 C62.69,2.11 75.76,7.87 78.74,19.49 C78.86,19.96 78.98,20.43 79.10,20.90 C79.86,21.13 80.61,21.36 81.37,21.59 C92.84,25.11 98.00,38.42 91.89,48.75 C91.65,49.17 91.40,49.58 91.16,50.00 C91.53,50.70 91.90,51.39 92.27,52.09 C97.89,62.69 92.13,75.76 80.51,78.74 C80.04,78.86 79.57,78.98 79.10,79.10 C78.87,79.86 78.64,80.61 78.41,81.37 C74.89,92.84 61.58,98.00 51.25,91.89 C50.83,91.65 50.42,91.40 50.00,91.16 C49.30,91.53 48.61,91.90 47.91,92.27 C37.31,97.89 24.24,92.13 21.26,80.51 C21.14,80.04 21.02,79.57 20.90,79.10 C20.14,78.87 19.39,78.64 18.63,78.41 C7.16,74.89 2.00,61.58 8.11,51.25 C8.35,50.83 8.60,50.42 8.84,50.00 C8.47,49.30 8.10,48.61 7.73,47.91 C2.11,37.31 7.87,24.24 19.49,21.26 C19.96,21.14 20.43,21.02 20.90,20.90 C21.13,20.14 21.36,19.39 21.59,18.63 C25.11,7.16 38.42,2.00 48.75,8.11 C49.17,8.35 49.58,8.60 50.00,8.84 Z</string>
<!-- MaterialShapes.Clover4Leaf -->
<string name="widget_shape_path_clover" translatable="false">M50.00,11.42 C50.46,11.06 50.93,10.71 51.39,10.35 C62.25,2.00 77.62,3.00 87.31,12.69 C97.00,22.38 98.00,37.75 89.65,48.61 C89.29,49.07 88.94,49.54 88.58,50.00 C88.94,50.46 89.29,50.93 89.65,51.39 C98.00,62.25 97.00,77.62 87.31,87.31 C77.62,97.00 62.25,98.00 51.39,89.65 C50.93,89.29 50.46,88.94 50.00,88.58 C49.54,88.94 49.07,89.29 48.61,89.65 C37.75,98.00 22.38,97.00 12.69,87.31 C3.00,77.62 2.00,62.25 10.35,51.39 C10.71,50.93 11.06,50.46 11.42,50.00 C11.06,49.54 10.71,49.07 10.35,48.61 C2.00,37.75 3.00,22.38 12.69,12.69 C22.38,3.00 37.75,2.00 48.61,10.35 C49.07,10.71 49.54,11.06 50.00,11.42 Z</string>
</resources>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="110dp"
android:minHeight="110dp"
android:minResizeWidth="110dp"
android:minResizeHeight="110dp"
android:targetCellWidth="2"
android:targetCellHeight="2"
android:updatePeriodMillis="1800000"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:description="@string/widget_analog_description"
android:configure="de.jeanlucmakiola.clockula.ui.widget.config.AnalogClockConfigActivity"
android:widgetFeatures="reconfigurable|configuration_optional"
android:previewLayout="@layout/widget_analog_plain" />
+4 -2
View File
@@ -9,5 +9,7 @@
android:updatePeriodMillis="0"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:previewImage="@mipmap/ic_launcher"
android:description="@string/widget_clock_description" />
android:description="@string/widget_clock_description"
android:configure="de.jeanlucmakiola.clockula.ui.widget.config.ClockWidgetConfigActivity"
android:widgetFeatures="reconfigurable|configuration_optional"
android:previewLayout="@layout/widget_digital_classic" />
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="40dp"
android:minHeight="40dp"
android:minResizeWidth="40dp"
android:minResizeHeight="40dp"
android:targetCellWidth="1"
android:targetCellHeight="1"
android:updatePeriodMillis="0"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:previewImage="@mipmap/ic_launcher"
android:description="@string/widget_quick_timer_description"
android:configure="de.jeanlucmakiola.clockula.ui.widget.config.QuickTimerConfigActivity"
android:widgetFeatures="reconfigurable|configuration_optional" />
@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="140dp"
android:minHeight="80dp"
android:minResizeWidth="110dp"
android:minResizeHeight="60dp"
android:targetCellWidth="3"
android:targetCellHeight="2"
android:updatePeriodMillis="0"
android:resizeMode="horizontal|vertical"
android:widgetCategory="home_screen"
android:previewImage="@mipmap/ic_launcher"
android:description="@string/widget_stopwatch_description" />