diff --git a/lib/pages/root/root_app.dart b/lib/pages/root/root_app.dart index 3a0bd6436..b2bd4620b 100644 --- a/lib/pages/root/root_app.dart +++ b/lib/pages/root/root_app.dart @@ -1,12 +1,11 @@ import 'dart:async'; -import 'package:collection/collection.dart'; +import 'package:fl_query/fl_query.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:internet_connection_checker/internet_connection_checker.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:spotube/collections/spotube_icons.dart'; import 'package:spotube/components/shared/dialogs/replace_downloaded_dialog.dart'; @@ -52,44 +51,43 @@ class RootApp extends HookConsumerWidget { }); final subscription = - InternetConnectionChecker().onStatusChange.listen((status) { - switch (status) { - case InternetConnectionStatus.connected: - scaffoldMessenger.showSnackBar( - SnackBar( - content: Row( - children: [ - Icon( - SpotubeIcons.wifi, - color: theme.colorScheme.onPrimary, - ), - const SizedBox(width: 10), - Text(context.l10n.connection_restored), - ], - ), - backgroundColor: theme.colorScheme.primary, - showCloseIcon: true, - width: 350, + QueryClient.connectivity.onConnectivityChanged.listen((status) { + if (status) { + scaffoldMessenger.showSnackBar( + SnackBar( + content: Row( + children: [ + Icon( + SpotubeIcons.wifi, + color: theme.colorScheme.onPrimary, + ), + const SizedBox(width: 10), + Text(context.l10n.connection_restored), + ], ), - ); - case InternetConnectionStatus.disconnected: - scaffoldMessenger.showSnackBar( - SnackBar( - content: Row( - children: [ - Icon( - SpotubeIcons.noWifi, - color: theme.colorScheme.onError, - ), - const SizedBox(width: 10), - Text(context.l10n.you_are_offline), - ], - ), - backgroundColor: theme.colorScheme.error, - showCloseIcon: true, - width: 300, + backgroundColor: theme.colorScheme.primary, + showCloseIcon: true, + width: 350, + ), + ); + } else { + scaffoldMessenger.showSnackBar( + SnackBar( + content: Row( + children: [ + Icon( + SpotubeIcons.noWifi, + color: theme.colorScheme.onError, + ), + const SizedBox(width: 10), + Text(context.l10n.you_are_offline), + ], ), - ); + backgroundColor: theme.colorScheme.error, + showCloseIcon: true, + width: 300, + ), + ); } }); diff --git a/lib/services/connectivity_adapter.dart b/lib/services/connectivity_adapter.dart index 6a3a46ee7..f5dc77374 100644 --- a/lib/services/connectivity_adapter.dart +++ b/lib/services/connectivity_adapter.dart @@ -1,12 +1,95 @@ +import 'dart:async'; +import 'dart:io'; + import 'package:fl_query/fl_query.dart'; -import 'package:internet_connection_checker/internet_connection_checker.dart'; +import 'package:flutter/widgets.dart'; + +class FlQueryInternetConnectionCheckerAdapter extends ConnectivityAdapter + with WidgetsBindingObserver { + final _connectionStreamController = StreamController.broadcast(); + + FlQueryInternetConnectionCheckerAdapter() : super() { + Timer.periodic(const Duration(minutes: 3), (timer) async { + if (WidgetsBinding.instance.lifecycleState == AppLifecycleState.paused) { + return; + } + await isConnected; + }); + } + + @override + didChangeAppLifecycleState(AppLifecycleState state) async { + if (state == AppLifecycleState.resumed) { + await isConnected; + } + } + + final vpnNames = [ + 'tun', + 'tap', + 'ppp', + 'pptp', + 'l2tp', + 'ipsec', + 'vpn', + 'wireguard', + 'openvpn', + 'softether', + 'proton', + 'strongswan', + 'cisco', + 'forticlient', + 'fortinet', + 'hideme', + 'hidemy', + 'hideman', + 'hidester', + 'lightway', + ]; + + Future isVpnActive() async { + final interfaces = await NetworkInterface.list( + includeLoopback: false, + type: InternetAddressType.any, + ); + + if (interfaces.isEmpty) { + return false; + } + + return interfaces.any( + (interface) => + vpnNames.any((name) => interface.name.toLowerCase().contains(name)), + ); + } + + Future doesConnectTo(String address) async { + try { + final result = await InternetAddress.lookup(address); + if (result.isNotEmpty && result[0].rawAddress.isNotEmpty) { + return true; + } + return false; + } on SocketException catch (_) { + return false; + } + } + + Future _isConnected() async { + return await doesConnectTo('google.com') || + await doesConnectTo('www.baidu.com') || // for China + await isVpnActive(); // when VPN is active that means we are connected + } -class FlQueryInternetConnectionCheckerAdapter extends ConnectivityAdapter { @override - Future get isConnected => InternetConnectionChecker().hasConnection; + Future get isConnected async { + final connected = await _isConnected(); + if (connected != isConnectedSync /*previous value*/) { + _connectionStreamController.add(connected); + } + return connected; + } @override - Stream get onConnectivityChanged => InternetConnectionChecker() - .onStatusChange - .map((status) => status == InternetConnectionStatus.connected); + Stream get onConnectivityChanged => _connectionStreamController.stream; } diff --git a/pubspec.lock b/pubspec.lock index 3e61a09dd..a3d2d0597 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1125,14 +1125,6 @@ packages: description: flutter source: sdk version: "0.0.0" - internet_connection_checker: - dependency: "direct main" - description: - name: internet_connection_checker - sha256: "1c683e63e89c9ac66a40748b1b20889fd9804980da732bf2b58d6d5456c8e876" - url: "https://pub.dev" - source: hosted - version: "1.0.0+1" intl: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index ab1dfe008..ae5b6a75d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -63,7 +63,6 @@ dependencies: html: ^0.15.1 http: ^1.1.0 image_picker: ^1.0.4 - internet_connection_checker: ^1.0.0+1 intl: ^0.18.0 introduction_screen: ^3.0.2 json_annotation: ^4.8.1