feat(fl_query_connectivity_plus_adapter): lookup based offline detection
This commit is contained in:
@@ -111,21 +111,21 @@ packages:
|
||||
path: ".."
|
||||
relative: true
|
||||
source: path
|
||||
version: "1.0.0-alpha.4"
|
||||
version: "1.0.0-alpha.5"
|
||||
fl_query_connectivity_plus_adapter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../fl_query_connectivity_plus_adapter"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.1.0-alpha.3"
|
||||
version: "0.1.0-alpha.4"
|
||||
fl_query_devtools:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../fl_query_devtools"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.1.0-alpha.2"
|
||||
version: "0.1.0-alpha.3"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:fl_query_connectivity_plus_adapter/lookups/connection_lookup_io.dart'
|
||||
if (dart.library.html) 'package:fl_query_connectivity_plus_adapter/lookups/connection_lookup_web.dart';
|
||||
|
||||
abstract class ConnectionLookup {
|
||||
Future<bool> doesConnectTo(String address);
|
||||
Future<bool> isVpnActive();
|
||||
}
|
||||
|
||||
class InternetConnectivityChecker
|
||||
with WidgetsBindingObserver, ConnectionLookupMixin {
|
||||
final StreamController<bool> controller;
|
||||
|
||||
bool _wasConnected = true;
|
||||
|
||||
bool get isAppPaused =>
|
||||
WidgetsBinding.instance.lifecycleState == AppLifecycleState.paused;
|
||||
|
||||
InternetConnectivityChecker(Duration duration)
|
||||
: controller = StreamController<bool>.broadcast() {
|
||||
Timer.periodic(duration, (timer) async {
|
||||
if (isAppPaused) {
|
||||
return;
|
||||
}
|
||||
await hasConnection();
|
||||
});
|
||||
Connectivity().onConnectivityChanged.listen((event) async {
|
||||
if (isAppPaused) {
|
||||
return;
|
||||
}
|
||||
await hasConnection();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
didChangeAppLifecycleState(AppLifecycleState state) async {
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
await hasConnection();
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> hasConnection() async {
|
||||
final connectivityResult = await (Connectivity().checkConnectivity());
|
||||
final hasNetwork = [
|
||||
ConnectivityResult.mobile,
|
||||
ConnectivityResult.wifi,
|
||||
ConnectivityResult.ethernet,
|
||||
].contains(connectivityResult);
|
||||
|
||||
final isVpn =
|
||||
connectivityResult == ConnectivityResult.vpn || await isVpnActive();
|
||||
|
||||
final connected = hasNetwork
|
||||
? kIsWeb
|
||||
? await doesConnectTo('/favicon.ico') ||
|
||||
await doesConnectTo('/favicon.png') ||
|
||||
await doesConnectTo('/favicon.gif')
|
||||
: await doesConnectTo('google.com') ||
|
||||
await doesConnectTo('www.baidu.com')
|
||||
: isVpn;
|
||||
|
||||
if (connected != _wasConnected) {
|
||||
_wasConnected = connected;
|
||||
controller.add(connected);
|
||||
}
|
||||
return connected;
|
||||
}
|
||||
|
||||
Stream<bool> get onConnectionChanged => controller.stream;
|
||||
}
|
||||
+8
-20
@@ -1,32 +1,20 @@
|
||||
library fl_query_connectivity_plus_adapter;
|
||||
|
||||
import 'package:fl_query/fl_query.dart';
|
||||
import 'package:connectivity_plus/connectivity_plus.dart';
|
||||
import 'package:fl_query_connectivity_plus_adapter/connection_checker.dart';
|
||||
|
||||
/// A [ConnectivityAdapter] that uses the `connectivity_plus` package.
|
||||
class FlQueryConnectivityPlusAdapter extends ConnectivityAdapter {
|
||||
final InternetConnectivityChecker _adapter;
|
||||
FlQueryConnectivityPlusAdapter({
|
||||
Duration pollingDuration = const Duration(minutes: 5),
|
||||
}) : _adapter = InternetConnectivityChecker(pollingDuration);
|
||||
|
||||
@override
|
||||
Future<bool> get isConnected async {
|
||||
final connection = await Connectivity().checkConnectivity();
|
||||
|
||||
switch (connection) {
|
||||
case ConnectivityResult.bluetooth:
|
||||
case ConnectivityResult.none:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
return await _adapter.hasConnection();
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<bool> get onConnectivityChanged =>
|
||||
Connectivity().onConnectivityChanged.map((event) {
|
||||
switch (event) {
|
||||
case ConnectivityResult.bluetooth:
|
||||
case ConnectivityResult.none:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
Stream<bool> get onConnectivityChanged => _adapter.onConnectionChanged;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fl_query_connectivity_plus_adapter/connection_checker.dart';
|
||||
|
||||
mixin ConnectionLookupMixin implements ConnectionLookup {
|
||||
@override
|
||||
Future<bool> doesConnectTo(String address) async {
|
||||
try {
|
||||
final result = await InternetAddress.lookup(address);
|
||||
return result.isNotEmpty && result[0].rawAddress.isNotEmpty;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static const _vpnNames = [
|
||||
'tun',
|
||||
'tap',
|
||||
'ppp',
|
||||
'pptp',
|
||||
'l2tp',
|
||||
'ipsec',
|
||||
'vpn',
|
||||
'wireguard',
|
||||
'openvpn',
|
||||
'softether',
|
||||
'proton',
|
||||
'strongswan',
|
||||
'cisco',
|
||||
'forticlient',
|
||||
'fortinet',
|
||||
'hideme',
|
||||
'hidemy',
|
||||
'hideman',
|
||||
'hidester',
|
||||
'lightway',
|
||||
];
|
||||
|
||||
@override
|
||||
Future<bool> 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)),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import 'dart:html';
|
||||
import 'package:fl_query_connectivity_plus_adapter/connection_checker.dart';
|
||||
|
||||
mixin ConnectionLookupMixin implements ConnectionLookup {
|
||||
@override
|
||||
Future<bool> doesConnectTo(String address) async {
|
||||
try {
|
||||
final url = address.startsWith("http") || address.startsWith("/")
|
||||
? address
|
||||
: "https://$address";
|
||||
final request = await HttpRequest.request(
|
||||
url,
|
||||
requestHeaders: {
|
||||
'Cache-Control': 'no-cache',
|
||||
'Pragma': 'no-cache',
|
||||
},
|
||||
);
|
||||
|
||||
return (request.status ?? 0) < 400;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isVpnActive() {
|
||||
return Future.value(false);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user