Maintainer changes on top of PR #2 (Naveax). Reduces the release to two deliberate connection modes and resolves the issues found in review: - Lock-screen privacy: move VISIBILITY_PRIVATE and a generic publicVersion into MainActivity.updateVpnNotification. The PR's fix lived in an unreferenced class and never reached the live notification. - Haptics: restore the default to off, and reduce HapticSettings to a single success() on connect/disconnect via the native channel. - SOCKS5: make it a real proxyOnly local-proxy mode rather than a TUN connection under a different label. TUN stays the default. - Health probe: restore the idle-only guard so /health is no longer polled every 30s while connected. - Recovery: add a _connectionEpoch counter so connect() aborts an in-flight Extreme recovery. - Startup restoration: rebuild the recovery snapshot from the still-active session after an app-process restart (TUN), and surface an honest orphaned state with a Reconnect action in proxy mode. Drops per-app routing entirely. It only ever functioned in TUN -- addDisallowedApplication lives inside the VPN-interface setup that proxyOnly skips -- and its allowlist half had no Dart caller. Also removes flutter_local_notifications, the global tap-haptic surface, and unused runtime telemetry. Co-Authored-By: Claude Opus 5 <[email protected]>
73 lines
2.0 KiB
Dart
73 lines
2.0 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
abstract final class HapticSettings {
|
|
HapticSettings._();
|
|
|
|
static const String _prefKey = 'haptic_feedback_enabled';
|
|
static const MethodChannel _channel =
|
|
MethodChannel('com.revoltvpn.app/haptics');
|
|
|
|
static bool _enabled = false;
|
|
static bool _initialized = false;
|
|
|
|
static bool get enabled => _enabled;
|
|
|
|
static Future<void> initialize() async {
|
|
if (_initialized) return;
|
|
|
|
try {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
_enabled = prefs.getBool(_prefKey) ?? false;
|
|
} catch (e) {
|
|
debugPrint('[Haptics] Failed to load preference: $e');
|
|
_enabled = false;
|
|
} finally {
|
|
_initialized = true;
|
|
}
|
|
}
|
|
|
|
static Future<void> setEnabled(bool value) async {
|
|
final previous = _enabled;
|
|
_enabled = value;
|
|
_initialized = true;
|
|
|
|
try {
|
|
final prefs = await SharedPreferences.getInstance();
|
|
final saved = await prefs.setBool(_prefKey, value);
|
|
if (!saved) throw StateError('Could not save haptic setting');
|
|
} catch (e) {
|
|
_enabled = previous;
|
|
debugPrint('[Haptics] Failed to save preference: $e');
|
|
rethrow;
|
|
}
|
|
}
|
|
|
|
/// Confirmation haptic for a successful connect/disconnect. Best-effort: a
|
|
/// vibration failure must never block or fail the VPN action.
|
|
static void success() => unawaited(_impact());
|
|
|
|
static Future<void> _impact() async {
|
|
if (!_initialized) await initialize();
|
|
if (!_enabled || kIsWeb) return;
|
|
|
|
if (defaultTargetPlatform == TargetPlatform.android) {
|
|
try {
|
|
final handled = await _channel.invokeMethod<bool>(
|
|
'impact',
|
|
<String, Object>{'kind': 'success'},
|
|
) ??
|
|
false;
|
|
if (handled) return;
|
|
} catch (e) {
|
|
debugPrint('[Haptics] Native vibration unavailable: $e');
|
|
}
|
|
}
|
|
|
|
await HapticFeedback.mediumImpact();
|
|
}
|
|
}
|