Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 5301 - added an erasing tool for proofs #5341

Merged
merged 2 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions packages/smooth_app/lib/background/background_task_add_price.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';

import 'package:crop_image/crop_image.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:http_parser/http_parser.dart';
Expand All @@ -11,6 +12,8 @@ import 'package:smooth_app/background/background_task_upload.dart';
import 'package:smooth_app/background/operation_type.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/pages/crop_parameters.dart';
import 'package:smooth_app/pages/prices/eraser_model.dart';
import 'package:smooth_app/pages/prices/eraser_painter.dart';

// TODO(monsieurtanuki): use transient file, in order to have instant access to proof image?
// TODO(monsieurtanuki): add source
Expand All @@ -33,6 +36,8 @@ class BackgroundTaskAddPrice extends BackgroundTask {
required this.currency,
required this.locationOSMId,
required this.locationOSMType,
// lines
required this.eraserCoordinates,
// multi
required this.barcode,
required this.priceIsDiscounted,
Expand All @@ -53,12 +58,25 @@ class BackgroundTaskAddPrice extends BackgroundTask {
locationOSMId = json[_jsonTagOSMId] as int,
locationOSMType =
LocationOSMType.fromOffTag(json[_jsonTagOSMType] as String)!,
eraserCoordinates =
_fromJsonListDouble(json[_jsonTagEraserCoordinates]),
barcode = json[_jsonTagBarcode] as String,
priceIsDiscounted = json[_jsonTagIsDiscounted] as bool,
price = json[_jsonTagPrice] as double,
priceWithoutDiscount = json[_jsonTagPriceWithoutDiscount] as double?,
super.fromJson(json);

static List<double>? _fromJsonListDouble(final List<dynamic>? input) {
if (input == null) {
return null;
}
final List<double> result = <double>[];
for (final dynamic item in input) {
result.add(item as double);
}
return result;
}

static const String _jsonTagImagePath = 'imagePath';
static const String _jsonTagRotation = 'rotation';
static const String _jsonTagX1 = 'x1';
Expand All @@ -67,6 +85,7 @@ class BackgroundTaskAddPrice extends BackgroundTask {
static const String _jsonTagY2 = 'y2';
static const String _jsonTagProofType = 'proofType';
static const String _jsonTagDate = 'date';
static const String _jsonTagEraserCoordinates = 'eraserCoordinates';
static const String _jsonTagCurrency = 'currency';
static const String _jsonTagOSMId = 'osmId';
static const String _jsonTagOSMType = 'osmType';
Expand All @@ -88,6 +107,7 @@ class BackgroundTaskAddPrice extends BackgroundTask {
final Currency currency;
final int locationOSMId;
final LocationOSMType locationOSMType;
final List<double>? eraserCoordinates;
final String barcode;
final bool priceIsDiscounted;
final double price;
Expand All @@ -107,6 +127,7 @@ class BackgroundTaskAddPrice extends BackgroundTask {
result[_jsonTagCurrency] = currency.name;
result[_jsonTagOSMId] = locationOSMId;
result[_jsonTagOSMType] = locationOSMType.offTag;
result[_jsonTagEraserCoordinates] = eraserCoordinates;
result[_jsonTagBarcode] = barcode;
result[_jsonTagIsDiscounted] = priceIsDiscounted;
result[_jsonTagPrice] = price;
Expand Down Expand Up @@ -185,6 +206,7 @@ class BackgroundTaskAddPrice extends BackgroundTask {
currency: currency,
locationOSMId: locationOSMId,
locationOSMType: locationOSMType,
eraserCoordinates: cropObject.eraserCoordinates,
barcode: barcode,
priceIsDiscounted: priceIsDiscounted,
price: price,
Expand Down Expand Up @@ -238,6 +260,16 @@ class BackgroundTaskAddPrice extends BackgroundTask {
..priceWithoutDiscount = priceWithoutDiscount
..productCode = barcode;

final List<Offset> offsets = <Offset>[];
if (eraserCoordinates != null) {
for (int i = 0; i < eraserCoordinates!.length; i += 2) {
final Offset offset = Offset(
eraserCoordinates![i],
eraserCoordinates![i + 1],
);
offsets.add(offset);
}
}
final String? path = await BackgroundTaskImage.cropIfNeeded(
fullPath: fullPath,
croppedPath: BackgroundTaskImage.getCroppedPath(fullPath),
Expand All @@ -246,6 +278,20 @@ class BackgroundTaskAddPrice extends BackgroundTask {
cropY1: cropY1,
cropX2: cropX2,
cropY2: cropY2,
overlayPainter: offsets.isEmpty
? null
: EraserPainter(
eraserModel: EraserModel(
rotation: CropRotationExtension.fromDegrees(rotationDegrees)!,
offsets: offsets,
),
cropRect: BackgroundTaskImage.getDownsizedRect(
cropX1,
cropY1,
cropX2,
cropY2,
),
),
);
if (path == null) {
// TODO(monsieurtanuki): maybe something more refined when we dismiss the picture, like alerting the user, though it's not supposed to happen anymore from upstream.
Expand Down
12 changes: 8 additions & 4 deletions packages/smooth_app/lib/background/background_task_image.dart
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ class BackgroundTaskImage extends BackgroundTaskUpload {
static Rect getUpsizedRect(final Rect source) =>
getResizedRect(source, _cropConversionFactor);

static Rect _getDownsizedRect(
static Rect getDownsizedRect(
final int cropX1,
final int cropY1,
final int cropX2,
Expand Down Expand Up @@ -234,6 +234,7 @@ class BackgroundTaskImage extends BackgroundTaskUpload {
required final int cropY1,
required final int cropX2,
required final int cropY2,
final CustomPainter? overlayPainter,
}) async {
final ui.Image full = await loadUiImage(
await (await BackgroundTaskUpload.getFile(fullPath)).readAsBytes());
Expand All @@ -246,11 +247,13 @@ class BackgroundTaskImage extends BackgroundTaskUpload {
return null;
}
// in that case, no need to crop
return fullPath;
if (overlayPainter == null) {
return fullPath;
}
}

Size getCroppedSize() {
final Rect cropRect = _getDownsizedRect(cropX1, cropY1, cropX2, cropY2);
final Rect cropRect = getDownsizedRect(cropX1, cropY1, cropX2, cropY2);
switch (CropRotationExtension.fromDegrees(rotationDegrees)!) {
case CropRotation.up:
case CropRotation.down:
Expand All @@ -272,11 +275,12 @@ class BackgroundTaskImage extends BackgroundTaskUpload {
return null;
}
final ui.Image cropped = await CropController.getCroppedBitmap(
crop: _getDownsizedRect(cropX1, cropY1, cropX2, cropY2),
crop: getDownsizedRect(cropX1, cropY1, cropX2, cropY2),
rotation: CropRotationExtension.fromDegrees(rotationDegrees)!,
image: full,
maxSize: null,
quality: FilterQuality.high,
overlayPainter: overlayPainter,
);
await saveJpeg(
file: await BackgroundTaskUpload.getFile(croppedPath),
Expand Down
87 changes: 31 additions & 56 deletions packages/smooth_app/lib/background/operation_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,66 +67,41 @@ enum OperationType {
'$_transientHeaderSeparator${work ?? ''}';
}

BackgroundTask fromJson(Map<String, dynamic> map) {
switch (this) {
case crop:
return BackgroundTaskCrop.fromJson(map);
case addPrice:
return BackgroundTaskAddPrice.fromJson(map);
case details:
return BackgroundTaskDetails.fromJson(map);
case hungerGames:
return BackgroundTaskHungerGames.fromJson(map);
case image:
return BackgroundTaskImage.fromJson(map);
case refreshLater:
return BackgroundTaskRefreshLater.fromJson(map);
case unselect:
return BackgroundTaskUnselect.fromJson(map);
case offline:
return BackgroundTaskOffline.fromJson(map);
case offlineBarcodes:
return BackgroundTaskTopBarcodes.fromJson(map);
case offlineProducts:
return BackgroundTaskDownloadProducts.fromJson(map);
case fullRefresh:
return BackgroundTaskFullRefresh.fromJson(map);
case languageRefresh:
return BackgroundTaskLanguageRefresh.fromJson(map);
}
}
BackgroundTask fromJson(Map<String, dynamic> map) => switch (this) {
crop => BackgroundTaskCrop.fromJson(map),
addPrice => BackgroundTaskAddPrice.fromJson(map),
details => BackgroundTaskDetails.fromJson(map),
hungerGames => BackgroundTaskHungerGames.fromJson(map),
image => BackgroundTaskImage.fromJson(map),
refreshLater => BackgroundTaskRefreshLater.fromJson(map),
unselect => BackgroundTaskUnselect.fromJson(map),
offline => BackgroundTaskOffline.fromJson(map),
offlineBarcodes => BackgroundTaskTopBarcodes.fromJson(map),
offlineProducts => BackgroundTaskDownloadProducts.fromJson(map),
fullRefresh => BackgroundTaskFullRefresh.fromJson(map),
languageRefresh => BackgroundTaskLanguageRefresh.fromJson(map),
};

bool matches(final TransientOperation action) =>
action.key.startsWith('$header$_transientHeaderSeparator');

String getLabel(final AppLocalizations appLocalizations) {
switch (this) {
case OperationType.details:
return appLocalizations.background_task_operation_details;
case OperationType.addPrice:
return 'Add price';
case OperationType.image:
return appLocalizations.background_task_operation_image;
case OperationType.unselect:
return 'Unselect a product image';
case OperationType.hungerGames:
return 'Answering to a Hunger Games question';
case OperationType.crop:
return 'Crop an existing image';
case OperationType.refreshLater:
return 'Waiting 10 min before refreshing product to get all automatic edits';
case OperationType.offline:
return 'Downloading top n products for offline usage';
case OperationType.offlineBarcodes:
return 'Downloading top n barcodes';
case OperationType.offlineProducts:
return 'Downloading products';
case OperationType.fullRefresh:
return 'Refreshing the full local database';
case OperationType.languageRefresh:
return 'Refreshing the local database to a new language';
}
}
String getLabel(final AppLocalizations appLocalizations) => switch (this) {
OperationType.details =>
appLocalizations.background_task_operation_details,
OperationType.addPrice => 'Add price',
OperationType.image => appLocalizations.background_task_operation_image,
OperationType.unselect => 'Unselect a product image',
OperationType.hungerGames => 'Answering to a Hunger Games question',
OperationType.crop => 'Crop an existing image',
OperationType.refreshLater =>
'Waiting 10 min before refreshing product to get all automatic edits',
OperationType.offline => 'Downloading top n products for offline usage',
OperationType.offlineBarcodes => 'Downloading top n barcodes',
OperationType.offlineProducts => 'Downloading products',
OperationType.fullRefresh => 'Refreshing the full local database',
OperationType.languageRefresh =>
'Refreshing the local database to a new language',
};

static int getSequentialId(final TransientOperation operation) {
final List<String> keyItems =
Expand Down
13 changes: 13 additions & 0 deletions packages/smooth_app/lib/pages/crop_helper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ abstract class CropHelper {
required final File smallCroppedFile,
required final Directory directory,
required final int sequenceNumber,
final List<Offset>? offsets,
});

/// Should we display the eraser with the crop grid?
bool get enableEraser;

/// Returns the crop rect according to local cropping method * factor.
@protected
Rect getLocalCropRect(final CropController controller) =>
Expand All @@ -43,8 +47,16 @@ abstract class CropHelper {
required final CropController controller,
required final File? fullFile,
required final File smallCroppedFile,
final List<Offset>? offsets,
}) {
final Rect cropRect = getLocalCropRect(controller);
final List<double> eraserCoordinates = <double>[];
if (offsets != null) {
for (final Offset offset in offsets) {
eraserCoordinates.add(offset.dx);
eraserCoordinates.add(offset.dy);
}
}
return CropParameters(
fullFile: fullFile,
smallCroppedFile: smallCroppedFile,
Expand All @@ -53,6 +65,7 @@ abstract class CropHelper {
y1: cropRect.top.ceil(),
x2: cropRect.right.floor(),
y2: cropRect.bottom.floor(),
eraserCoordinates: eraserCoordinates,
);
}

Expand Down
Loading
Loading