mirror of
https://github.com/kuhyx/screen-locker.git
synced 2026-11-10 18:07:22 +01:00
The rest banner used to be inserted into the column only while a break ran, so its arrival took height from the exercise list, shifted every tile down and pushed the last one into scroll range. The strip is now a permanently reserved 64 dp slot (muted "Rest / 00:00" while idle, Skip keeps its space), the list is a non-scrolling column that scales down as a whole if it would not fit (0.96 on the Pixel 6a with four tiles, pinned by a widget test at the phone's logical size with the status bar simulated), and the activity is locked to portrait. Verified on the real phone: only the tapped circle's pixels change when a rest starts. Verifying that needed a workout screen that today's already-logged workout did not offer, so the app gained a `sandbox` product flavor beside `daily`: com.kuhy.workout_app.sandbox installs side by side with its own data, refuses every network connection at the socket layer, starts no LAN server for the PC to pull from, keeps its /sdcard files beside the daily build's, wears a red SANDBOX ribbon, and offers a SANDBOX section in Settings (wipe, mark today not done, rest length in seconds). MainActivity answers the flavor and carries a `WorkoutSandbox` logcat trace of every tap, break, save and navigation. On Linux the same mode is WORKOUT_SANDBOX=1, driven under Xvfb by scripts/run_sandbox_xvfb.sh. CI builds --flavor daily; .phone-deploy-flavor tells phone_deploy.sh the same. Set circles and the home icon buttons carry semantics labels so they can be addressed by element (and by screen readers). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YbBZqUaNWyhHYfzz6zGC7q
67 lines
2.8 KiB
Dart
67 lines
2.8 KiB
Dart
/// The sandbox flavor: a second install that can be poked at without fear.
|
|
///
|
|
/// `com.kuhy.workout_app.sandbox` is a separate package, so Android already
|
|
/// keeps its database, secure storage and notifications apart from the daily
|
|
/// build. This class carries the rest of the isolation into Dart: no network,
|
|
/// no LAN server for the PC to pull from, its own `/sdcard` paths, and a
|
|
/// rest length short enough to watch a break start and end in one sitting.
|
|
library;
|
|
|
|
import 'dart:io' show Platform;
|
|
|
|
import 'package:flutter/foundation.dart' show debugPrint;
|
|
import 'package:flutter/services.dart';
|
|
|
|
/// Build-time facts about the running flavor, resolved once at startup.
|
|
abstract final class Sandbox {
|
|
/// Channel MainActivity answers on; also carries the logcat trace.
|
|
static const MethodChannel channel = MethodChannel(
|
|
'com.kuhy.workout_app/sandbox',
|
|
);
|
|
|
|
/// Where the sandbox mirrors its backups, beside — never inside — the
|
|
/// daily build's `/sdcard/WorkoutTracker`.
|
|
static const String backupDir = '/sdcard/WorkoutTrackerSandbox';
|
|
|
|
/// Where the sandbox writes finished workouts; the PC never reads this.
|
|
static const String syncFilePath = '/sdcard/workout_result_sandbox.json';
|
|
|
|
/// Rest length used when no override has been saved yet.
|
|
static const int defaultRestSecs = 5;
|
|
|
|
/// Whether this process is the sandbox flavor. Set once by [init]; tests
|
|
/// assign it directly to exercise both flavors in one process.
|
|
static bool enabled = false;
|
|
|
|
/// Rest length (seconds) every break uses while [enabled]; persisted by
|
|
/// the Settings screen, loaded by `main`.
|
|
static int restSecs = defaultRestSecs;
|
|
|
|
/// Environment variable that turns the sandbox on where there is no
|
|
/// flavor to ask — the Linux desktop build, run under Xvfb to check a
|
|
/// change on the PC before it goes anywhere near the phone.
|
|
static const String envVar = 'WORKOUT_SANDBOX';
|
|
|
|
/// Asks the platform which flavor built this process, or, where there is
|
|
/// no MainActivity to ask (Linux, tests), reads [envVar].
|
|
static Future<void> init([Map<String, String>? environment]) async {
|
|
try {
|
|
enabled = await channel.invokeMethod<bool>('isSandbox') ?? false;
|
|
} on MissingPluginException catch (error) {
|
|
enabled = (environment ?? Platform.environment)[envVar] == '1';
|
|
debugPrint(
|
|
'WorkoutApp: no sandbox channel on this platform ($error) — '
|
|
'$envVar=${enabled ? '1: sandbox' : 'unset: daily build'}.',
|
|
);
|
|
}
|
|
}
|
|
|
|
/// [defaultSecs] for the daily build, the override for the sandbox.
|
|
static int rest(int defaultSecs) => enabled ? restSecs : defaultSecs;
|
|
|
|
/// [defaultLabel] for the daily build; the sandbox names its real length
|
|
/// so a 5 s countdown is never captioned "3 min".
|
|
static String restLabel(String defaultLabel) =>
|
|
enabled ? 'Rest ($restSecs s — sandbox)' : defaultLabel;
|
|
}
|