Android 5.1 and modern Java¶
The Moto X runs Android 5.1, API level 22. Four version concepts must remain separate:
- Gradle JDK runs Gradle and the Android Gradle Plugin on the development computer.
- Java source compatibility controls Java language syntax accepted in app source.
- Java library availability controls classes and methods available at runtime; core-library desugaring backports a documented subset.
- Android API level controls framework APIs such as Activities, Bluetooth, storage, and notification behavior.
This project uses a current Android Gradle Plugin, Java 17 source and target
compatibility, core-library desugaring, compileSdk = 36, and minSdk = 22.
Modern syntax is not the same as an unrestricted Java 17 runtime. Lambdas,
method references, try-with-resources, and many newer language constructs can
be transformed by D8/R8. Supported java.time, collection, stream, and other
library APIs can be supplied by desugar_jdk_libs. Arbitrary JDK modules,
desktop APIs, reflection behavior, and every post-Java-8 API are not thereby
available.
Likewise, compiling against API 36 permits references to new Android APIs but does not put them on Android 5.1. Calls newer than API 22 require an API-level guard and a working fallback, or they must not be used.
Practical rules:
- let Android lint inspect every change;
- keep API-22 tests and a physical-device smoke test in the release path;
- prefer platform APIs and small dependencies;
- check every dependency's minimum SDK before adding it;
- do not raise
minSdkmerely to accommodate a convenience library; - do not use hidden/private Android APIs.
Dashboard HTTPS trust¶
The dashboard transport keeps the platform's system root certificates and
supplements them with the bundled ISRG Root X1 and ISRG Root YR certificates.
It filters out user-installed CAs, applies the resulting socket factory only to
connections for the configured API origin, and retains normal certificate
chain and hostname verification. It does not pin the renewable server leaf or
replace the process-wide TLS defaults. The DER resources are SHA-256 checked at
startup against fingerprints in TrustAnchorSet; the fingerprints and
validity periods were checked against Let's Encrypt's Chains of Trust and
official certificate downloads on
2026-10-01; the local DER fingerprints match those official downloads. The
self-signed X1 and YR certificates have notAfter dates 2035-06-04 and
2045-09-02, respectively. These certificate dates differ from trust-store
policy: Let's Encrypt currently lists X1 trust until 2030-06-04 and YR as not
yet included in root-program trust stores. The app adds both roots for its
configured API transport. This is an app-scoped compatibility path; installing
a root in Android's user certificate store is not required by the app.
The API-22 acceptance run on 2026-10-01 passed 31 instrumented tests on the
XT1058. Its OriginTrust cases verify a successful request to the configured
API, the API-22 platform-default failure, continued trust of another system
root, and rejection of wrong-host, expired, self-signed, and untrusted-root
certificates. Certificate-renewal acceptance, modern-device transport, and a
real production-feed request remain follow-up checks.
Centering against asymmetric system-bar insets¶
A deployment check on the Moto X Play XT1563 running LineageOS 17.1 / Android 10 (API 29) found a modern-device layout issue:
- the window frame covers the full 1920×1080 display;
- Android reports a 144-pixel right navigation-bar content inset;
- the root application View is consequently 1776×1080; and
- centering within that View puts the clock about 72 pixels left of the physical panel center.
The unused strip is black and blends into the clock background, so this is a minor visual defect rather than a functional failure.
Do not fix it by assuming that every Android 10 device has a 144-pixel inset. Navigation mode, ROM, orientation, density, and which side owns the navigation bar can all change the value. For a right inset \(I_{\text{right}}\) and left inset \(I_{\text{left}}\), the horizontal error is
which is 72 pixels for the measured device. The app does not compute this from
WindowInsets; it measures the resulting geometry instead.
Implemented approach¶
CenteredFrameController (package display.frame) observes the root View's
layout changes and system UI visibility changes. Each time it reads three
measurements: the root View's size, its getLocationOnScreen origin, and the
real panel size from Display.getRealSize (API 17). The pure
CenteredContentFrame.compute function then returns the largest rectangle
inside the root that is symmetric about the physical display center on each
axis. With root origin \(x_0\), root width \(w\), and display width \(W\), the
display center in root coordinates is \(c=W/2-x_0\), and the frame spans
For the Moto X Play, \(c=960\), \(w=1776\), \(h=816\), giving the span \([144,1776)\); a left-side inset mirrors it, and an unobstructed root yields the full width. The vertical axis uses the same rule. Degenerate geometry (zero sizes, or a root that does not contain the display center) falls back to the full root rectangle.
The two clock faces live in a content_frame FrameLayout; the controller
writes the frame's margins, and only when they change, so the extra layout pass
settles immediately. The faces keep their own burn-in reserved margins inside
the frame, and the control overlay and Preview panel stay outside it.
Why measuring avoids double-application
The rectangle depends only on where the root actually sits on the panel, never on a previously applied correction. Feeding the frame back in as a root returns the same center, which the unit tests assert. A rectangle that is already symmetric is returned unchanged.
Verification status¶
- Unit tests (
CenteredContentFrameTest) cover the Moto X Play right inset, the mirrored left inset, equal and zero insets, status-bar and bottom insets, odd sizes, degenerate input, and idempotence. They prove the geometry only; they do not exercise Views or the Android window system. - Moto X XT1058 (Android 5.1): the root fills the display, so the frame equals
the root. A launch screenshot with the controller attached is byte-identical
to one with it disabled, and no layout loop appears in
logcat. The "Performed 6 layouts in a row"WindowManagermessage at launch also appears without the controller and is not caused by it. - Moto X Play XT1563 (LineageOS 17.1 / Android 10, API 29): the physical
DisplayFrameAcceptanceTestpassed on 2 October 2026. With system bars exposed, it measured a 1920×1080 panel, a 1776×1080 root, and a frame at x=144 with width 1632; the frame center is x=960. Restoring immersive mode returned both root and frame to 1920×1080 without a second correction. The full 33-test instrumented suite passed. The clock artwork intentionally moves within the frame for burn-in protection (an anchor up to about four percent of display width plus a smaller periodic shift), so a single screenshot does not measure the frame's center.
Remaining acceptance checks:
- physical left-side navigation placement is not available on this ROM; mirrored geometry and idempotence remain covered by JVM tests;
- longer-running theme, burn-in, control-overlay, and Preview visual checks remain part of the broader bedside soak.
Android 10 also enforces a cleartext policy that Android 5.1 did not apply to
the device-local HTTP test fixture. The debug variant now allows cleartext
only for 127.0.0.1 through a debug-only network security configuration. The
app's origin policy still rejects other HTTP hosts, and the release manifest
contains no cleartext exception. Production cards continue to use HTTPS.