Bump version to 1.0.2+4, implement native state restoration, and clean up UI
This commit is contained in:
1 parent
61be397058
commit
208da38586
4 files changed
+34
-69
No files matched your search
@@ -22,31 +22,6 @@ class ClockDisplay extends StatelessWidget {
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
// Background quota track ring
|
||||
const CustomPaint(
|
||||
size: Size(200, 200),
|
||||
painter: _ArcPainter(
|
||||
progress: 1.0,
|
||||
color: Color.fromRGBO(74, 85, 104, 0.2),
|
||||
strokeWidth: 4,
|
||||
),
|
||||
),
|
||||
|
||||
// Live quota progress ring
|
||||
TweenAnimationBuilder<double>(
|
||||
tween: Tween(begin: 0, end: timer.progress),
|
||||
duration: const Duration(milliseconds: 400),
|
||||
builder: (context, value, _) => CustomPaint(
|
||||
size: const Size(200, 200),
|
||||
painter: _ArcPainter(
|
||||
progress: value,
|
||||
color: _cyanGlow,
|
||||
strokeWidth: 4,
|
||||
glow: true,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
// Center content: countdown + speed
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -94,45 +69,3 @@ class ClockDisplay extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _ArcPainter extends CustomPainter {
|
||||
final double progress;
|
||||
final Color color;
|
||||
final double strokeWidth;
|
||||
final bool glow;
|
||||
|
||||
const _ArcPainter({
|
||||
required this.progress,
|
||||
required this.color,
|
||||
this.strokeWidth = 4,
|
||||
this.glow = false,
|
||||
});
|
||||
|
||||
@override
|
||||
void paint(Canvas canvas, Size size) {
|
||||
final rect = Rect.fromLTWH(0, 0, size.width, size.height)
|
||||
.deflate(strokeWidth / 2);
|
||||
|
||||
if (glow) {
|
||||
final glowPaint = Paint()
|
||||
..color = const Color.fromRGBO(0, 229, 255, 0.15)
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth + 8
|
||||
..strokeCap = StrokeCap.round
|
||||
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 8);
|
||||
canvas.drawArc(rect, -pi / 2, 2 * pi * progress, false, glowPaint);
|
||||
}
|
||||
|
||||
final paint = Paint()
|
||||
..color = color
|
||||
..style = PaintingStyle.stroke
|
||||
..strokeWidth = strokeWidth
|
||||
..strokeCap = StrokeCap.round;
|
||||
|
||||
canvas.drawArc(rect, -pi / 2, 2 * pi * progress, false, paint);
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldRepaint(covariant _ArcPainter old) =>
|
||||
old.progress != progress || old.color != color;
|
||||
}
|
||||
@@ -27,7 +27,19 @@ class SessionTimer extends ChangeNotifier {
|
||||
int _lastUsedBytes = 0;
|
||||
double _currentSpeedKbps = 0.0; // Total speed (RX+TX)
|
||||
|
||||
SessionTimer({required this.vpnConnection});
|
||||
SessionTimer({required this.vpnConnection}) {
|
||||
vpnConnection.addListener(_onVpnConnectionChanged);
|
||||
}
|
||||
|
||||
/// Fires whenever VpnConnection state changes.
|
||||
/// Only acts during startup restoration — normal connects go through ConnectButton.
|
||||
void _onVpnConnectionChanged() {
|
||||
if (vpnConnection.status == VpnStatus.connected &&
|
||||
!isRunning &&
|
||||
vpnConnection.isStartupRestoration) {
|
||||
start('resume');
|
||||
}
|
||||
}
|
||||
|
||||
int get remaining => _remainingSeconds;
|
||||
bool get isRunning => _timer != null && _timer!.isActive;
|
||||
@@ -181,6 +193,7 @@ class SessionTimer extends ChangeNotifier {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
vpnConnection.removeListener(_onVpnConnectionChanged);
|
||||
_timer?.cancel();
|
||||
VpnNotificationManager.cancel();
|
||||
super.dispose();
|
||||
|
||||
@@ -43,6 +43,11 @@ class VpnConnection extends ChangeNotifier {
|
||||
VpnStatus _status = VpnStatus.disconnected;
|
||||
VpnStatus get status => _status;
|
||||
|
||||
// True only during the brief window after startup where we detected an
|
||||
// already-running tunnel — lets SessionTimer know to auto-resume.
|
||||
bool _isStartupRestoration = false;
|
||||
bool get isStartupRestoration => _isStartupRestoration;
|
||||
|
||||
// Short message shown to the user under the connect button
|
||||
String _statusMessage = 'Tap to connect';
|
||||
String get statusMessage => _statusMessage;
|
||||
@@ -84,6 +89,20 @@ class VpnConnection extends ChangeNotifier {
|
||||
try {
|
||||
_stageSub = _wireguard.vpnStageSnapshot.listen(_mapStage);
|
||||
} catch (_) {}
|
||||
|
||||
// Check if the VPN was already running before the app started.
|
||||
// This handles the case where the user swiped the app away and reopened it.
|
||||
try {
|
||||
final currentStage = await _wireguard.stage();
|
||||
if (currentStage == VpnStage.connected) {
|
||||
_isStartupRestoration = true;
|
||||
_mapStage(currentStage);
|
||||
// Clear the flag after a few seconds — only needed during app startup
|
||||
Future.delayed(const Duration(seconds: 5), () {
|
||||
_isStartupRestoration = false;
|
||||
});
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
// ── Native State Mapping ──────────────────────────────────────────────────
|
||||
|
||||
Reference in new issue
Block a user