mirror of
https://github.com/KRTirtho/spotube.git
synced 2025-09-13 07:55:18 +00:00

* 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
116 lines
2.7 KiB
Dart
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;
|
|
}
|