Add explicit TUN and SOCKS5 connection modes, app routing controls, authenticated per-session local SOCKS5, Android network monitoring, persistent haptics, resilient session recovery, notification hardening and client-side Hivemind cleanup. Keep the hosted flutter_vless_android 1.1.5 runtime as the base and apply the Android compatibility/security patches at build time instead of restoring the unstable vendored runtime.
66 lines
1.9 KiB
Dart
66 lines
1.9 KiB
Dart
import 'package:flutter/gestures.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:revoltvpn/logic/haptic_settings.dart';
|
|
|
|
/// Gives short touch taps the same haptic feedback across the app.
|
|
/// Scroll gestures are ignored by checking travel distance and touch duration.
|
|
class AppHapticSurface extends StatefulWidget {
|
|
final Widget child;
|
|
|
|
const AppHapticSurface({super.key, required this.child});
|
|
|
|
@override
|
|
State<AppHapticSurface> createState() => _AppHapticSurfaceState();
|
|
}
|
|
|
|
class _PointerStart {
|
|
final Offset position;
|
|
final DateTime time;
|
|
|
|
const _PointerStart(this.position, this.time);
|
|
}
|
|
|
|
class _AppHapticSurfaceState extends State<AppHapticSurface> {
|
|
static const double _maxTapTravel = 18;
|
|
static const Duration _maxTapDuration = Duration(milliseconds: 700);
|
|
|
|
final Map<int, _PointerStart> _starts = <int, _PointerStart>{};
|
|
|
|
bool _supportsHaptics(PointerDeviceKind kind) {
|
|
return kind == PointerDeviceKind.touch ||
|
|
kind == PointerDeviceKind.stylus ||
|
|
kind == PointerDeviceKind.invertedStylus;
|
|
}
|
|
|
|
void _onDown(PointerDownEvent event) {
|
|
if (!_supportsHaptics(event.kind)) return;
|
|
_starts[event.pointer] = _PointerStart(event.position, DateTime.now());
|
|
}
|
|
|
|
void _onCancel(PointerCancelEvent event) {
|
|
_starts.remove(event.pointer);
|
|
}
|
|
|
|
void _onUp(PointerUpEvent event) {
|
|
final start = _starts.remove(event.pointer);
|
|
if (start == null || !_supportsHaptics(event.kind)) return;
|
|
|
|
final travelled = (event.position - start.position).distance;
|
|
final elapsed = DateTime.now().difference(start.time);
|
|
if (travelled <= _maxTapTravel && elapsed <= _maxTapDuration) {
|
|
HapticSettings.tap();
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Listener(
|
|
behavior: HitTestBehavior.translucent,
|
|
onPointerDown: _onDown,
|
|
onPointerUp: _onUp,
|
|
onPointerCancel: _onCancel,
|
|
child: widget.child,
|
|
);
|
|
}
|
|
}
|