Skip to content

Commit

Permalink
feat: Added the logic to save the user drawn badge to local storage. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
Jhalakupadhyay authored Aug 3, 2024
1 parent 7595618 commit e9b2418
Show file tree
Hide file tree
Showing 10 changed files with 304 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@ class DataToByteArrayConverter {
for (int x = 0; x < chunks.length; x++) {
ans.add(hexStringToByteArray(chunks[x]));
}
logger.d("ans ka length ${ans[0].length}");
return ans;
}

Expand Down
131 changes: 131 additions & 0 deletions lib/bademagic_module/utils/file_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import 'dart:io';
import 'dart:convert';
import 'dart:typed_data';
import 'package:badgemagic/bademagic_module/utils/byte_array_utils.dart';
import 'package:badgemagic/bademagic_module/utils/image_utils.dart';
import 'package:badgemagic/providers/imageprovider.dart';
import 'package:flutter/services.dart';
import 'package:get_it/get_it.dart';
import 'package:path_provider/path_provider.dart';
import 'package:uuid/uuid.dart';

class FileHelper {
final InlineImageProvider imageCacheProvider =
GetIt.instance<InlineImageProvider>();
ImageUtils imageUtils = ImageUtils();
static const Uuid uuid = Uuid();

static Future<String> _getFilePath(String filename) async {
final directory = await getApplicationDocumentsDirectory();
return '${directory.path}/$filename';
}

static Future<File> _writeToFile(String filename, String data) async {
final path = await _getFilePath(filename);

logger.d('Writing to file: $path');

return File(path).writeAsString(data);
}

// static Future<String> _readFromFile(String filename) async {
// try {
// final path = await _getFilePath(filename);
// return await File(path).readAsString();
// } catch (e) {
// return ''; // Return an empty string if there's an error
// }
// }

static String _generateUniqueFilename() {
final String uniqueId = uuid.v4();
final String timestamp = DateTime.now().millisecondsSinceEpoch.toString();
return 'data_${timestamp}_$uniqueId.json';
}

// Add a new image to the cache
void addToCache(Uint8List imageData) {
int key;
if (imageCacheProvider.availableKeys.isNotEmpty) {
// Reuse the lowest available key
key = imageCacheProvider.availableKeys.first;
imageCacheProvider.availableKeys.remove(key);
} else {
// Assign a new key
key = imageCacheProvider.imageCache.length;
while (imageCacheProvider.imageCache.containsKey(key)) {
key++;
}
}
imageCacheProvider.imageCache[key] = imageData;
}

// Remove an image from the cache
void removeFromCache(int key) {
if (imageCacheProvider.imageCache.containsKey(key)) {
imageCacheProvider.imageCache.remove(key);
imageCacheProvider.availableKeys
.add(key); // Add key to the pool of available keys
}
}

// Generate a Uint8List from a 2D list (image data) and add it to the cache
Future<void> _addImageDataToCache(List<List<dynamic>> imageData) async {
// Convert List<List<dynamic>> to List<List<int>>
List<List<int>> intImageData =
imageData.map((list) => list.cast<int>()).toList();
Uint8List imageBytes =
await imageUtils.convert2DListToUint8List(intImageData);
addToCache(imageBytes);
}

// Read all files, parse the 2D lists, and add to cache
Future<void> loadImageCacheFromFiles() async {
final directory = await getApplicationDocumentsDirectory();
final List<FileSystemEntity> files = directory.listSync();

for (var file in files) {
if (file is File && file.path.endsWith('.json')) {
final String content = await file.readAsString();
if (content.isNotEmpty) {
// Ensure correct type casting
final List<dynamic> decodedData = jsonDecode(content);
final List<List<dynamic>> imageData =
decodedData.cast<List<dynamic>>();
await _addImageDataToCache(imageData);
}
}
}
}

// Save a 2D list to a file with a unique name
Future<void> saveImage(List<List<bool>> imageData) async {
List<List<int>> image = List.generate(
imageData.length, (i) => List<int>.filled(imageData[i].length, 0));

//convert the 2D list of bool into 2D list of int
for (int i = 0; i < imageData.length; i++) {
for (int j = 0; j < imageData[i].length; j++) {
image[i][j] = imageData[i][j] ? 1 : 0;
}
}

// Generate a unique filename
String filename = _generateUniqueFilename();

logger.d('Saving image to file: $filename');

// Convert the 2D list to JSON string
String jsonData = jsonEncode(image);

logger.d('JSON data: $jsonData');

// Write the JSON string to a file
await _writeToFile(filename, jsonData);

logger.d('Image saved to file: $filename');

//Add the image to the image cache after saving it to a file
await _addImageDataToCache(image);
}
}
48 changes: 48 additions & 0 deletions lib/bademagic_module/utils/image_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,54 @@ class ImageUtils {

late ui.Picture picture;

//convert the 2D list to Uint8List
//this funcction will be ustilised to convert the user drawn badge to Uint8List
//and thus will be able to display with other vectors in the badge
Future<Uint8List> convert2DListToUint8List(List<List<int>> twoDList) async {
int height = twoDList.length;
int width = twoDList[0].length;

// Create a buffer to hold the pixel data
Uint8List pixels =
Uint8List(width * height * 4); // 4 bytes per pixel (RGBA)

for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int value = twoDList[y][x] == 1 ? 0 : 255;
int offset = (y * width + x) * 4;
pixels[offset] = value; // Red
pixels[offset + 1] = value; // Green
pixels[offset + 2] = value; // Blue
pixels[offset + 3] = 255; // Alpha
}
}

// Create an ImmutableBuffer from the pixel data
ui.ImmutableBuffer buffer = await ui.ImmutableBuffer.fromUint8List(pixels);

// Create an ImageDescriptor from the buffer
ui.ImageDescriptor descriptor = ui.ImageDescriptor.raw(
buffer,
width: width,
height: height,
pixelFormat: ui.PixelFormat.rgba8888,
);

// Instantiate a codec
ui.Codec codec = await descriptor.instantiateCodec();

// Get the first frame from the codec
ui.FrameInfo frameInfo = await codec.getNextFrame();

// Get the image from the frame
ui.Image image = frameInfo.image;

// Convert the image to PNG format
ByteData? pngBytes = await image.toByteData(format: ui.ImageByteFormat.png);

return pngBytes!.buffer.asUint8List();
}

//function that generates the Picture from the given asset
Future<void> _loadSVG(String asset) async {
//loading the Svg from the assets
Expand Down
6 changes: 6 additions & 0 deletions lib/providers/imageprovider.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:convert';
import 'dart:ui' as ui;
import 'package:badgemagic/bademagic_module/utils/byte_array_utils.dart';
import 'package:badgemagic/bademagic_module/utils/file_helper.dart';
import 'package:badgemagic/bademagic_module/utils/image_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Expand All @@ -9,6 +10,9 @@ class InlineImageProvider extends ChangeNotifier {
//boolean variable to check for isCacheInitialized
bool isCacheInitialized = false;

//set of available keys
Set<int> availableKeys = {};

//list of vectors
List<String> vectors = [];

Expand Down Expand Up @@ -54,6 +58,7 @@ class InlineImageProvider extends ChangeNotifier {
//function that generates the image cache
//it fills the map with the Unit8List(byte Array) of the images
Future<void> generateImageCache() async {
FileHelper fileHelper = FileHelper();
await initVectors();
for (int x = 0; x < vectors.length; x++) {
ui.Image image = await imageUtils.generateImageView(vectors[x]);
Expand All @@ -62,6 +67,7 @@ class InlineImageProvider extends ChangeNotifier {
var unit8List = byteData!.buffer.asUint8List();
imageCache[x] = unit8List;
}
fileHelper.loadImageCacheFromFiles();
notifyListeners();
}

Expand Down
13 changes: 7 additions & 6 deletions lib/view/draw_badge_screen.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'package:badgemagic/bademagic_module/utils/file_helper.dart';
import 'package:badgemagic/constants.dart';
import 'package:badgemagic/providers/drawbadge_provider.dart';
import 'package:badgemagic/view/widgets/common_scaffold_widget.dart';
import 'package:badgemagic/virtualbadge/view/draw_badge.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get_it/get_it.dart';
import 'package:provider/provider.dart';

class DrawBadge extends StatefulWidget {
Expand All @@ -16,8 +16,6 @@ class DrawBadge extends StatefulWidget {
}

class _DrawBadgeState extends State<DrawBadge> {
DrawBadgeProvider cellStateToggle = GetIt.instance<DrawBadgeProvider>();

@override
void initState() {
SystemChrome.setPreferredOrientations([
Expand All @@ -34,13 +32,14 @@ class _DrawBadgeState extends State<DrawBadge> {
DeviceOrientation.landscapeLeft,
DeviceOrientation.landscapeRight,
]);
cellStateToggle.resetGrid();
super.dispose();
}

@override
Widget build(BuildContext context) {
DrawBadgeProvider drawToggle = Provider.of<DrawBadgeProvider>(context);
DrawBadgeProvider drawToggle =
Provider.of<DrawBadgeProvider>(context, listen: false);
FileHelper fileHelper = FileHelper();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky);
return CommonScaffold(
title: 'BadgeMagic',
Expand Down Expand Up @@ -126,7 +125,9 @@ class _DrawBadgeState extends State<DrawBadge> {
),
),
TextButton(
onPressed: () {},
onPressed: () {
fileHelper.saveImage(drawToggle.getGrid());
},
child: const Column(
children: [
Icon(
Expand Down
3 changes: 3 additions & 0 deletions lib/view/homescreen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:badgemagic/bademagic_module/utils/image_utils.dart';
import 'package:badgemagic/constants.dart';
import 'package:badgemagic/providers/badge_message_provider.dart';
import 'package:badgemagic/providers/cardsprovider.dart';
import 'package:badgemagic/providers/drawbadge_provider.dart';
import 'package:badgemagic/providers/imageprovider.dart';
import 'package:badgemagic/view/special_text_field.dart';
import 'package:badgemagic/view/widgets/common_scaffold_widget.dart';
Expand Down Expand Up @@ -31,10 +32,12 @@ class _HomeScreenState extends State<HomeScreen> with TickerProviderStateMixin {
InlineImageProvider inlineImageProvider =
GetIt.instance<InlineImageProvider>();
Converters converters = Converters();
DrawBadgeProvider drawBadgeProvider = GetIt.instance<DrawBadgeProvider>();
bool isPrefixIconClicked = false;

@override
void initState() {
drawBadgeProvider.resetGrid();
SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
]);
Expand Down
1 change: 1 addition & 0 deletions lib/view/widgets/vectorview.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class _VectorGridViewState extends State<VectorGridView> {
borderRadius: BorderRadius.circular(5),
),
surfaceTintColor: Colors.white,
color: Colors.white,
elevation: 5,
child: Padding(
padding: const EdgeInsets.all(10.0),
Expand Down
2 changes: 2 additions & 0 deletions macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import FlutterMacOS
import Foundation

import flutter_blue_plus
import path_provider_foundation

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FlutterBluePlusPlugin.register(with: registry.registrar(forPlugin: "FlutterBluePlusPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
}
Loading

0 comments on commit e9b2418

Please sign in to comment.