mc_gallery/lib/features/core/services/connection_service.dart

143 lines
4.5 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/foundation.dart';
import 'package:internet_connection_checker/internet_connection_checker.dart';
import 'package:mc_gallery/features/core/services/logging_service.dart';
import '/locator.dart';
/// Used to observe the current connection type.
class ConnectionService with LoggingService {
ConnectionService({
required Connectivity connectivity,
required InternetConnectionChecker internetConnectionChecker,
bool shouldInitialize = true,
}) : _connectivity = connectivity,
_internetConnectionChecker = internetConnectionChecker {
if (shouldInitialize) initialize();
}
ConnectivityResult? _connectivityResult;
ConnectivityResult? get connectivityResult => _connectivityResult;
final Connectivity _connectivity;
final Map<String, StreamSubscription> _connectivitySubscriptions = {};
final InternetConnectionChecker _internetConnectionChecker;
final Completer _isInitialized = Completer();
Completer<bool>? _hasInternetConnection;
final ValueNotifier<bool> _hasInternetConnectionListenable = ValueNotifier(false);
ValueListenable<bool> get hasInternetConnectionListenable => _hasInternetConnectionListenable;
Future<bool> get hasInternetConnection async {
try {
if (_hasInternetConnection == null) {
_hasInternetConnection = Completer<bool>();
try {
final hasInternetConnection = await _internetConnectionChecker.hasConnection;
_hasInternetConnection!.complete(hasInternetConnection);
return hasInternetConnection;
} catch (error) {
_hasInternetConnection!.complete(false);
return false;
} finally {
_hasInternetConnection = null;
}
} else {
final awaitedHasInternet = await _hasInternetConnection!.future;
return awaitedHasInternet;
}
} on SocketException catch (_, __) {
return false;
}
}
Future<void> initialize() async {
try {
final tag = runtimeType.toString();
if (_connectivitySubscriptions[tag] != null) {
await dispose();
}
_connectivitySubscriptions[tag] = _connectivity.onConnectivityChanged.listen(
_onConnectivityChanged,
cancelOnError: false,
onError: (error, stack) {},
);
await _onConnectivityChanged(await _connectivity.checkConnectivity());
_internetConnectionChecker.onStatusChange.listen((final event) {
switch (event) {
case InternetConnectionStatus.connected:
_hasInternetConnectionListenable.value = true;
break;
case InternetConnectionStatus.disconnected:
_hasInternetConnectionListenable.value = false;
break;
}
});
_isInitialized.complete();
} catch (error, stackTrace) {
handle(error, stackTrace);
}
}
Future<void> dispose() async {
for (final subscription in _connectivitySubscriptions.values) {
await subscription.cancel();
}
_connectivitySubscriptions.clear();
_connectivityResult = null;
}
Future<void> addListener({
required String tag,
required Future<void> Function({
required ConnectivityResult connectivityResult,
required bool hasInternet,
})
listener,
bool tryCallListenerOnAdd = true,
}) async {
try {
if (_connectivityResult != null && tryCallListenerOnAdd)
await listener(
connectivityResult: _connectivityResult!, hasInternet: await hasInternetConnection);
_connectivitySubscriptions[tag] =
_connectivity.onConnectivityChanged.listen((connectivityResult) async {
await listener(
connectivityResult: connectivityResult, hasInternet: await hasInternetConnection);
});
} catch (error, stackTrace) {
handle(error, stackTrace);
}
}
Future<void> removeListener({required String tag}) async {
try {
final subscription = _connectivitySubscriptions[tag];
if (subscription != null) {
await subscription.cancel();
} else {}
} catch (error, stackTrace) {
handle(error, stackTrace);
}
}
Future<void> _onConnectivityChanged(ConnectivityResult connectivityResult) async {
try {
_connectivityResult = connectivityResult;
} catch (error, stackTrace) {
handle(error, stackTrace);
}
}
static ConnectionService get locate => Locator.locate();
}
class NoInternetException implements Exception {}