import 'dart:convert'; import 'dart:typed_data'; import 'package:aitrainer_app/service/logging.dart'; import 'package:aitrainer_app/util/not_found_exception.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:aitrainer_app/library//network_image_to_byte.dart'; import 'dart:collection'; class ImageCache with Logging { static final ImageCache _singleton = ImageCache._internal(); final LinkedHashMap _images = LinkedHashMap(); final LinkedHashMap _imageMap = LinkedHashMap(); var downloadSize = 0; // Create storage final storage = FlutterSecureStorage(); factory ImageCache() { return _singleton; } ImageCache._internal(); LinkedHashMap getImageList() => _images; void clearImageList() => _images.clear(); bool existsImageInMap(int id, String url) { final String imageKey = generateMd5(url + "_" + id.toString()); return _imageMap[imageKey] != null && _imageMap[imageKey] == true; } String? getImageString(int id, String url) { final String imageKey = generateMd5(url + "_" + id.toString()); return _images[imageKey]; } Future putImageToList(int id, String url) async { final String imageKey = generateMd5(url + "_" + id.toString()); // get from storage final String? imageString = await getImageAs64BaseString(id, url); if (imageString != null) { _images[imageKey] = imageString; _imageMap[imageKey] = true; } } Future saveImageToPrefs(String key, String value) async { //log(" save the key " + key); await storage.write(key: key, value: value); return; } Future emptyPrefs() async { await storage.deleteAll(); return; } Future loadImageFromPrefs(String key) async { String? value = await storage.read(key: key); return value; } Future existImageInPrefs(String key) async { return await storage.containsKey(key: key); } // encodes bytes list as string static String base64String(Uint8List data) { return base64Encode(data); } // decode bytes from a string Image imageFrom64BaseString(String base64String) { return Image.memory( base64Decode(base64String), fit: BoxFit.fill, ); } Future getImage(int id, String name) async { if (name.length == 0) { return null; } final String imageKey = generateMd5(name + "_" + id.toString()); final String? imageString = await storage.read(key: imageKey); if (imageString == null) { return null; } final Image image = imageFrom64BaseString(imageString); return image; } Future? getImageAs64BaseString(int id, String name) async { final String imageKey = generateMd5(name + "_" + id.toString()); String? imageString; if (await storage.containsKey(key: imageKey)) { //log(" .. get from storage"); imageString = await storage.read(key: imageKey); } else { imageString = await downloadAndSaveImage(id, name); //log(" .. downloaded"); } return imageString; } Future downloadAndSaveImage(int id, String url) async { final String imageKey = generateMd5(url + "_" + id.toString()); if (!await existImageInPrefs(imageKey)) { try { if (url.contains("http")) { log(" ... direct download " + url); Uint8List byteImage = await networkImageToByte(url); this.downloadSize += byteImage.length; final String imageAsString = base64String(byteImage); ImageCache().saveImageToPrefs(imageKey, imageAsString); return imageAsString; } } on NotFoundException catch (_) { print(url + " not found"); } on Exception catch (e) { print(e); } } else { //log(" .. from storage"); //await storage.read(key: imageKey); } return null; } String generateMd5(String input) { String converted = input.replaceAll(RegExp(r'\.'), 'a'); converted = converted.replaceAll(RegExp(r'\/'), 'b'); converted = converted.replaceAll(RegExp(r':'), 'c'); //print("key: " + converted); return converted; } }