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
45 lines
1.5 KiB
Dart
45 lines
1.5 KiB
Dart
/// The sandbox's trace: every tap, break, save and navigation, to logcat.
|
|
library;
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:workout_app/sandbox/sandbox.dart';
|
|
|
|
/// Writes one line per event under the `WorkoutSandbox` logcat tag.
|
|
///
|
|
/// A no-op in the daily build, so call sites can stay unconditional. Lines
|
|
/// also go through [debugPrint], which is what `flutter run` shows.
|
|
abstract final class SandboxLog {
|
|
static bool _channelDown = false;
|
|
|
|
/// Records [event] with optional [details], e.g.
|
|
/// `SandboxLog.event('tap set', {'exercise': 'Squat', 'set': 2})`.
|
|
static void event(String event, [Map<String, Object?> details = const {}]) {
|
|
if (!Sandbox.enabled) return;
|
|
final line = details.isEmpty ? event : '$event $details';
|
|
debugPrint('WorkoutSandbox: $line');
|
|
unawaited(_toLogcat(line));
|
|
}
|
|
|
|
static Future<void> _toLogcat(String line) async {
|
|
if (_channelDown) return;
|
|
try {
|
|
await Sandbox.channel.invokeMethod<void>('log', line);
|
|
} on MissingPluginException catch (error) {
|
|
// Said once, then the trace lives in debugPrint alone: repeating this
|
|
// on every event would drown the trace it is warning about.
|
|
_channelDown = true;
|
|
debugPrint(
|
|
'WorkoutSandbox: logcat channel unavailable ($error) — the trace '
|
|
'continues in flutter logs only.',
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Forgets a failed channel so the next event retries it.
|
|
@visibleForTesting
|
|
static void resetForTesting() => _channelDown = false;
|
|
}
|