spotube/lib/services/connectivity_adapter.dart
Kingkor Roy Tirtho 6673e5a8a8
feat: improved caching based on riverpod (#1343)
* feat: add riverpod based favorite album provider

* feat: add album is saved, new releases and tracks providers

* feat: add artist related providers

* feat: add all categories providers

* feat: add lyrics provider

* feat: add playlist related providers

* feat: add search provider

* feat: add view and spotify friends provider

* feat: add playlist create and update and favorite handlers

* feat: use providers in home screen

* chore: fix dart lint issues

* feat: use new providers for playlist and albums screen

* feat: use providers in artist page

* feat: use providers on library page

* feat: use provider for playlist and album card and heart button

* feat: use provider in search page

* feat: use providers in generate playlist

* feat: use provider in lyrics screen

* feat: use provider for create playlist

* feat: use provider in add track dialog

* feat: use providers in remaining pages and remove fl_query

* fix: remove direct access to provider.value

* fix: glitching when loading

* fix: user album loading next page indicator

* feat: make many provider autoDispose after 5 minutes of no usage

* fix: ignore episodes in tracks
2024-03-20 23:38:39 +06:00

116 lines
2.7 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/widgets.dart';
class ConnectionCheckerService with WidgetsBindingObserver {
final _connectionStreamController = StreamController<bool>.broadcast();
final Dio dio;
static final _instance = ConnectionCheckerService._();
static ConnectionCheckerService get instance => _instance;
ConnectionCheckerService._() : dio = Dio() {
Timer? timer;
onConnectivityChanged.listen((connected) {
if (!connected && timer == null) {
timer = Timer.periodic(const Duration(seconds: 30), (timer) async {
if (WidgetsBinding.instance.lifecycleState ==
AppLifecycleState.paused) {
return;
}
await isConnected;
});
} else {
timer?.cancel();
timer = null;
}
});
}
@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<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)),
);
}
Future<bool> 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 (_) {
try {
final response = await dio.head('https://$address');
return (response.statusCode ?? 500) <= 400;
} on DioException catch (_) {
return false;
}
}
}
Future<bool> _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
}
bool isConnectedSync = false;
Future<bool> get isConnected async {
final connected = await _isConnected();
isConnectedSync = connected;
if (connected != isConnectedSync /*previous value*/) {
_connectionStreamController.add(connected);
}
return connected;
}
Stream<bool> get onConnectivityChanged => _connectionStreamController.stream;
}