chatapp3-flutter/lib/shared/tools/sc_network_image_utils.dart
2026-04-21 13:44:03 +08:00

131 lines
3.7 KiB
Dart

import 'dart:async';
import 'dart:io';
import 'dart:ui';
import 'package:extended_image/extended_image.dart';
import 'package:flutter/painting.dart';
import 'package:yumi/app/constants/sc_global_config.dart';
import 'package:yumi/shared/data_sources/sources/local/user_manager.dart';
bool _requiresAuthenticatedImageHeaders(String url) {
return url.contains("/external/oss/local/");
}
Map<String, String>? buildNetworkImageHeaders(String url) {
if (!_requiresAuthenticatedImageHeaders(url)) {
return null;
}
final uri = Uri.tryParse(url);
final headers = <String, String>{
"req-lang": SCGlobalConfig.lang,
"X-Forwarded-Proto": "http",
"User-Agent": "Dart/3.7.2(dart:io)",
"req-imei": SCGlobalConfig.imei,
"req-app-intel":
"version=${SCGlobalConfig.version};build=${SCGlobalConfig.build};model=${SCGlobalConfig.model};sysVersion=${SCGlobalConfig.sysVersion};channel=${SCGlobalConfig.channel}",
"req-client": Platform.isAndroid ? "Android" : "iOS",
"req-sys-origin":
"origin=${SCGlobalConfig.origin};originChild=${SCGlobalConfig.originChild}",
};
if ((uri?.host ?? "").isNotEmpty) {
headers["Host"] = uri!.host;
}
final token = AccountStorage().getToken();
if (token.isNotEmpty) {
headers["Authorization"] = "Bearer $token";
}
headers.removeWhere((key, value) => value.trim().isEmpty);
return headers;
}
int? _resolveImageProviderCacheDimension(double? logicalSize) {
if (logicalSize == null || !logicalSize.isFinite || logicalSize <= 0) {
return null;
}
final devicePixelRatio =
PlatformDispatcher.instance.implicitView?.devicePixelRatio ?? 2.0;
final maxDimension = SCGlobalConfig.isLowPerformanceDevice ? 1280 : 1920;
final pixelSize = (logicalSize * devicePixelRatio).round();
return pixelSize.clamp(1, maxDimension).toInt();
}
ImageProvider<Object> buildCachedImageProvider(
String url, {
double? logicalWidth,
double? logicalHeight,
}) {
late final ImageProvider<Object> provider;
if (url.startsWith("http")) {
provider = ExtendedNetworkImageProvider(
url,
headers: buildNetworkImageHeaders(url),
cache: true,
cacheMaxAge: const Duration(days: 7),
);
} else if (url.startsWith('/')) {
provider = FileImage(File(url));
} else {
provider = AssetImage(url);
}
return ResizeImage.resizeIfNeeded(
_resolveImageProviderCacheDimension(logicalWidth),
_resolveImageProviderCacheDimension(logicalHeight),
provider,
);
}
Future<bool> warmImageResource(
String resource, {
double? logicalWidth,
double? logicalHeight,
Duration timeout = const Duration(seconds: 6),
}) async {
if (resource.isEmpty) {
return false;
}
final provider = buildCachedImageProvider(
resource,
logicalWidth: logicalWidth,
logicalHeight: logicalHeight,
);
final stream = provider.resolve(ImageConfiguration.empty);
final completer = Completer<bool>();
late final ImageStreamListener listener;
listener = ImageStreamListener(
(image, synchronousCall) {
if (!completer.isCompleted) {
completer.complete(true);
}
stream.removeListener(listener);
},
onError: (Object error, StackTrace? stackTrace) {
if (!completer.isCompleted) {
completer.complete(false);
}
stream.removeListener(listener);
},
);
stream.addListener(listener);
return completer.future.timeout(
timeout,
onTimeout: () {
stream.removeListener(listener);
return false;
},
);
}
Future<bool> warmNetworkImage(
String url, {
double? logicalWidth,
double? logicalHeight,
Duration timeout = const Duration(seconds: 6),
}) {
return warmImageResource(
url,
logicalWidth: logicalWidth,
logicalHeight: logicalHeight,
timeout: timeout,
);
}