Files
ReVoltVPN/lib/logic/haptic_settings.dart
esefxdzandClaude Opus 5 6fcde9dd7b refactor(app): debloat 3.3.1 and fix startup restoration
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]>
2026-08-31 23:31:51 +03:00

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();
}
}