fix:增加图片选择和拍照功能
This commit is contained in:
@@ -43,6 +43,8 @@ class DeviceHeaderService {
|
||||
String _androidOaid = '';
|
||||
String _androidImei = '';
|
||||
String _androidMac = '';
|
||||
IosDeviceInfo? iosDeviceInfo;
|
||||
AndroidDeviceInfo? androidDeviceInfo;
|
||||
Future<void>? _baseInitialization;
|
||||
Future<void>? _identityInitialization;
|
||||
|
||||
@@ -120,9 +122,23 @@ class DeviceHeaderService {
|
||||
_iosIdfv = await _resolveIdfv();
|
||||
await _collectIosPaidIfMissing();
|
||||
}
|
||||
await getDeviceInfo();
|
||||
} catch (_) {}
|
||||
}
|
||||
|
||||
Future<void> getDeviceInfo() async {
|
||||
try {
|
||||
final deviceInfoPlugin = DeviceInfoPlugin();
|
||||
if (Platform.isIOS) {
|
||||
iosDeviceInfo = await deviceInfoPlugin.iosInfo;
|
||||
} else if (Platform.isAndroid) {
|
||||
androidDeviceInfo = await deviceInfoPlugin.androidInfo;
|
||||
}
|
||||
} catch (e) {
|
||||
print('$e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _collectAndroidIdIfAgreed() async {
|
||||
if (_androidId.isNotEmpty) return;
|
||||
if (!StorageService.to.getBool(storagePrivacyAgreed)) return; // 隐私同意后才采集
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import '../../features/assets/data/asset_models.dart';
|
||||
|
||||
class UploadRepository {
|
||||
const UploadRepository();
|
||||
|
||||
Future<UploadResult> uploadAssetImage(AssetImage image) async {
|
||||
// TODO: Connect to upload backend.
|
||||
return UploadResult(
|
||||
remoteUrl: image.remoteUrl,
|
||||
thumbnailUrl: image.thumbnailUrl,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UploadResult {
|
||||
const UploadResult({this.remoteUrl, this.thumbnailUrl});
|
||||
|
||||
final String? remoteUrl;
|
||||
final String? thumbnailUrl;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import '../../features/assets/data/asset_models.dart';
|
||||
import 'upload_repository.dart';
|
||||
|
||||
class UploadService {
|
||||
const UploadService({required this.repository});
|
||||
|
||||
final UploadRepository repository;
|
||||
|
||||
Future<UploadResult> uploadAssetImage(AssetImage image) async {
|
||||
// Placeholder: future implementation will upload the file and return URLs.
|
||||
return repository.uploadAssetImage(image);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import 'dart:io';
|
||||
import 'dart:ui' as ui;
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
|
||||
import 'package:gal/gal.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:jinzhi_flutter_app/common/services/device_header_service.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
class ImageUtil {
|
||||
static const String _albumName = 'jinzhi';
|
||||
|
||||
static Stream<FileResponse> downloadImage(String url, String key) {
|
||||
return DefaultCacheManager().getFileStream(
|
||||
url,
|
||||
key: key,
|
||||
withProgress: true,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<String> downloadImageInFile(String url, String key) async {
|
||||
final file = await DefaultCacheManager().getSingleFile(url, key: key);
|
||||
return file.path;
|
||||
}
|
||||
|
||||
static Future<bool> requestForImagePermission() async {
|
||||
if (Platform.isIOS) {
|
||||
PermissionStatus photoStatus = await Permission.photosAddOnly.status;
|
||||
if (photoStatus != PermissionStatus.granted) {
|
||||
photoStatus = await Permission.photosAddOnly.request();
|
||||
if (photoStatus != PermissionStatus.granted) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (Platform.isAndroid) {
|
||||
int? sdkInt = DeviceHeaderService.to.androidDeviceInfo?.version.sdkInt;
|
||||
if (sdkInt != null && sdkInt >= 33) {
|
||||
// ✅ Android 13+ 不需要读权限,直接返回 true
|
||||
// gal 插件内部通过 MediaStore 写入
|
||||
return true;
|
||||
} else {
|
||||
// ✅ Android 10-12 仍可能需要 storage 权限
|
||||
final status = await Permission.storage.request();
|
||||
return status.isGranted;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static Future<void> saveImage(FileInfo fileInfo) async {
|
||||
await Gal.putImage(fileInfo.file.path, album: _albumName);
|
||||
}
|
||||
|
||||
static Future<void> saveImageData(ByteData byteData) async {
|
||||
Uint8List imageBytes = byteData.buffer.asUint8List();
|
||||
await Gal.putImageBytes(
|
||||
imageBytes,
|
||||
name: 'XWallpaper_share',
|
||||
album: _albumName,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> saveImageUint8List(Uint8List imageBytes) async {
|
||||
await Gal.putImageBytes(
|
||||
imageBytes,
|
||||
name: 'XWallpaper_share',
|
||||
album: _albumName,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> saveUIImageData(ui.Image image) async {
|
||||
final byteData = await image.toByteData(format: ui.ImageByteFormat.png);
|
||||
Uint8List imageBytes = byteData!.buffer.asUint8List();
|
||||
await Gal.putImageBytes(
|
||||
imageBytes,
|
||||
name: 'XWallpaper_share',
|
||||
album: _albumName,
|
||||
);
|
||||
}
|
||||
|
||||
static Future<Uint8List> createTransparentImage(double aspectRatio) async {
|
||||
double minSize = 100;
|
||||
|
||||
double width = minSize * aspectRatio;
|
||||
double height = minSize;
|
||||
|
||||
final recorder = PictureRecorder();
|
||||
final canvas = Canvas(
|
||||
recorder,
|
||||
Rect.fromLTWH(0, 0, width.toDouble(), height.toDouble()),
|
||||
);
|
||||
final paint = Paint()..color = Colors.transparent;
|
||||
canvas.drawRect(
|
||||
Rect.fromLTWH(0.0, 0.0, width.toDouble(), height.toDouble()),
|
||||
paint,
|
||||
);
|
||||
|
||||
final picture = recorder.endRecording();
|
||||
final img = await picture.toImage(width.toInt(), height.toInt());
|
||||
final pngBytes = await img.toByteData(format: ImageByteFormat.png);
|
||||
|
||||
return pngBytes!.buffer.asUint8List();
|
||||
}
|
||||
|
||||
// 下载图片并返回 Uint8List(简单实现)
|
||||
static Future<Uint8List?> fetchThumbBytes(String? url) async {
|
||||
if (url == null || url.isEmpty) return null;
|
||||
try {
|
||||
final resp = await http.get(Uri.parse(url));
|
||||
if (resp.statusCode == 200) return resp.bodyBytes;
|
||||
} catch (_) {}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user