Refactor to simplify
This commit is contained in:
@@ -1,8 +1,6 @@
|
||||
import "dart:async";
|
||||
import "dart:developer" show log;
|
||||
import "dart:typed_data";
|
||||
|
||||
import "package:flutter/cupertino.dart";
|
||||
import "package:flutter/foundation.dart" show kDebugMode;
|
||||
import "package:flutter/material.dart";
|
||||
import "package:logging/logging.dart";
|
||||
@@ -14,7 +12,6 @@ import "package:photos/models/ml/face/face.dart";
|
||||
import "package:photos/models/ml/face/person.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_detection/detection.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/face_filtering/face_filtering_constants.dart";
|
||||
import "package:photos/services/machine_learning/face_ml/feedback/cluster_feedback.dart";
|
||||
import "package:photos/services/machine_learning/ml_service.dart";
|
||||
import "package:photos/services/search_service.dart";
|
||||
import "package:photos/theme/ente_theme.dart";
|
||||
@@ -31,7 +28,6 @@ class FileInfoFaceWidget extends StatefulWidget {
|
||||
final PersonEntity? person;
|
||||
final String? clusterID;
|
||||
final bool highlight;
|
||||
final bool editMode;
|
||||
|
||||
const FileInfoFaceWidget(
|
||||
this.file,
|
||||
@@ -40,7 +36,6 @@ class FileInfoFaceWidget extends StatefulWidget {
|
||||
this.person,
|
||||
this.clusterID,
|
||||
this.highlight = false,
|
||||
this.editMode = false,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@@ -56,11 +51,6 @@ class _FileInfoFaceWidgetState extends State<FileInfoFaceWidget> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final bool givenFaces = widget.faceCrops != null;
|
||||
return _buildFaceImageGenerated(givenFaces);
|
||||
}
|
||||
|
||||
Widget _buildFaceImageGenerated(bool givenFaces) {
|
||||
late final mlDataDB = MLDataDB.instance;
|
||||
return FutureBuilder<Map<String, Uint8List>?>(
|
||||
future: givenFaces
|
||||
? widget.faceCrops
|
||||
@@ -71,97 +61,7 @@ class _FileInfoFaceWidgetState extends State<FileInfoFaceWidget> {
|
||||
MemoryImage(snapshot.data![widget.face.faceID]!);
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
if (widget.editMode) return;
|
||||
|
||||
log(
|
||||
"FaceWidget is tapped, with person ${widget.person?.data.name} and clusterID ${widget.clusterID}",
|
||||
name: "FaceWidget",
|
||||
);
|
||||
if (widget.person == null && widget.clusterID == null) {
|
||||
// Double check that it doesn't belong to an existing clusterID.
|
||||
final existingClusterID =
|
||||
await mlDataDB.getClusterIDForFaceID(widget.face.faceID);
|
||||
if (existingClusterID != null) {
|
||||
final fileIdsToClusterIds =
|
||||
await mlDataDB.getFileIdToClusterIds();
|
||||
final files =
|
||||
await SearchService.instance.getAllFilesForSearch();
|
||||
final clusterFiles = files
|
||||
.where(
|
||||
(file) =>
|
||||
fileIdsToClusterIds[file.uploadedFileID]
|
||||
?.contains(existingClusterID) ??
|
||||
false,
|
||||
)
|
||||
.toList();
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ClusterPage(
|
||||
clusterFiles,
|
||||
clusterID: existingClusterID,
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (widget.face.score <= kMinimumQualityFaceScore) {
|
||||
// The face score is too low for automatic clustering,
|
||||
// assigning a manual new clusterID so that the user can cluster it manually
|
||||
final String clusterID = newClusterID();
|
||||
await mlDataDB.updateFaceIdToClusterId(
|
||||
{widget.face.faceID: clusterID},
|
||||
);
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ClusterPage(
|
||||
[widget.file],
|
||||
clusterID: clusterID,
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
showShortToast(
|
||||
context,
|
||||
S.of(context).faceNotClusteredYet,
|
||||
);
|
||||
unawaited(MLService.instance.clusterAllImages(force: true));
|
||||
return;
|
||||
}
|
||||
if (widget.person != null) {
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PeoplePage(
|
||||
person: widget.person!,
|
||||
searchResult: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else if (widget.clusterID != null) {
|
||||
final fileIdsToClusterIds =
|
||||
await mlDataDB.getFileIdToClusterIds();
|
||||
final files =
|
||||
await SearchService.instance.getAllFilesForSearch();
|
||||
final clusterFiles = files
|
||||
.where(
|
||||
(file) =>
|
||||
fileIdsToClusterIds[file.uploadedFileID]
|
||||
?.contains(widget.clusterID) ??
|
||||
false,
|
||||
)
|
||||
.toList();
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ClusterPage(
|
||||
clusterFiles,
|
||||
clusterID: widget.clusterID!,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
onTap: _onTap,
|
||||
child: Column(
|
||||
children: [
|
||||
Stack(
|
||||
@@ -195,66 +95,10 @@ class _FileInfoFaceWidgetState extends State<FileInfoFaceWidget> {
|
||||
),
|
||||
),
|
||||
),
|
||||
// TODO: the edges of the green line are still not properly rounded around ClipRRect
|
||||
if (widget.editMode)
|
||||
Positioned(
|
||||
right: 0,
|
||||
top: 0,
|
||||
child: GestureDetector(
|
||||
onTap: _cornerIconPressed,
|
||||
child: isJustRemoved
|
||||
? const Icon(
|
||||
CupertinoIcons.add_circled_solid,
|
||||
color: Colors.green,
|
||||
)
|
||||
: const Icon(
|
||||
Icons.cancel,
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (widget.person != null)
|
||||
Text(
|
||||
widget.person!.data.isIgnored
|
||||
? '(' + S.of(context).ignored + ')'
|
||||
: widget.person!.data.name.trim(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
if (kDebugMode)
|
||||
Text(
|
||||
'S: ${widget.face.score.toStringAsFixed(3)} (I)',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
maxLines: 1,
|
||||
),
|
||||
if (kDebugMode)
|
||||
Text(
|
||||
'B: ${widget.face.blur.toStringAsFixed(0)} (I)',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
maxLines: 1,
|
||||
),
|
||||
if (kDebugMode)
|
||||
Text(
|
||||
'D: ${widget.face.detection.getFaceDirection().toDirectionString()} (I)',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
maxLines: 1,
|
||||
),
|
||||
if (kDebugMode)
|
||||
Text(
|
||||
'Sideways: ${widget.face.detection.faceIsSideways().toString()} (I)',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
maxLines: 1,
|
||||
),
|
||||
if (kDebugMode && widget.face.score < kMinimumFaceShowScore)
|
||||
Text(
|
||||
'Not visible to user (I)',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
maxLines: 1,
|
||||
),
|
||||
..._buildFaceInfo(),
|
||||
],
|
||||
),
|
||||
);
|
||||
@@ -287,24 +131,120 @@ class _FileInfoFaceWidgetState extends State<FileInfoFaceWidget> {
|
||||
);
|
||||
}
|
||||
|
||||
void _cornerIconPressed() async {
|
||||
log('face widget (file info) corner icon is pressed');
|
||||
try {
|
||||
if (isJustRemoved) {
|
||||
await ClusterFeedbackService.instance
|
||||
.addFacesToCluster([widget.face.faceID], widget.clusterID!);
|
||||
} else {
|
||||
await ClusterFeedbackService.instance
|
||||
.removeFilesFromCluster([widget.file], widget.clusterID!);
|
||||
}
|
||||
|
||||
setState(() {
|
||||
isJustRemoved = !isJustRemoved;
|
||||
});
|
||||
} catch (e, s) {
|
||||
_logger.severe(
|
||||
"removing face/file from cluster from file info widget failed: $e, \n $s",
|
||||
List<Widget> _buildFaceInfo() {
|
||||
final List<Widget> faceInfo = [];
|
||||
if (widget.person != null) {
|
||||
faceInfo.add(
|
||||
Text(
|
||||
widget.person!.data.isIgnored
|
||||
? '(' + S.of(context).ignored + ')'
|
||||
: widget.person!.data.name.trim(),
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
),
|
||||
);
|
||||
}
|
||||
if (kDebugMode) {
|
||||
faceInfo.add(
|
||||
Text(
|
||||
'S: ${widget.face.score.toStringAsFixed(3)} (I)',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
maxLines: 1,
|
||||
),
|
||||
);
|
||||
faceInfo.add(
|
||||
Text(
|
||||
'B: ${widget.face.blur.toStringAsFixed(0)} (I)',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
maxLines: 1,
|
||||
),
|
||||
);
|
||||
faceInfo.add(
|
||||
Text(
|
||||
'D: ${widget.face.detection.getFaceDirection().toDirectionString()} (I)',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
maxLines: 1,
|
||||
),
|
||||
);
|
||||
faceInfo.add(
|
||||
Text(
|
||||
'Sideways: ${widget.face.detection.faceIsSideways().toString()} (I)',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
maxLines: 1,
|
||||
),
|
||||
);
|
||||
if (widget.face.score < kMinimumFaceShowScore) {
|
||||
faceInfo.add(
|
||||
Text(
|
||||
'Not visible to user (I)',
|
||||
style: Theme.of(context).textTheme.bodySmall,
|
||||
maxLines: 1,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return faceInfo;
|
||||
}
|
||||
|
||||
Future<void> _onTap() async {
|
||||
final mlDataDB = MLDataDB.instance;
|
||||
if (widget.person != null) {
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => PeoplePage(
|
||||
person: widget.person!,
|
||||
searchResult: null,
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
final String? clusterID = widget.clusterID ??
|
||||
await mlDataDB.getClusterIDForFaceID(widget.face.faceID);
|
||||
if (clusterID != null) {
|
||||
final fileIdsToClusterIds = await mlDataDB.getFileIdToClusterIds();
|
||||
final files = await SearchService.instance.getAllFilesForSearch();
|
||||
final clusterFiles = files
|
||||
.where(
|
||||
(file) =>
|
||||
fileIdsToClusterIds[file.uploadedFileID]?.contains(clusterID) ??
|
||||
false,
|
||||
)
|
||||
.toList();
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ClusterPage(
|
||||
clusterFiles,
|
||||
clusterID: clusterID,
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (widget.face.score <= kMinimumQualityFaceScore) {
|
||||
// The face score is too low for automatic clustering,
|
||||
// assigning a manual new clusterID so that the user can cluster it manually
|
||||
final String clusterID = newClusterID();
|
||||
await mlDataDB.updateFaceIdToClusterId(
|
||||
{widget.face.faceID: clusterID},
|
||||
);
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ClusterPage(
|
||||
[widget.file],
|
||||
clusterID: clusterID,
|
||||
),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
showShortToast(
|
||||
context,
|
||||
S.of(context).faceNotClusteredYet,
|
||||
);
|
||||
unawaited(MLService.instance.clusterAllImages(force: true));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ class FacesItemWidget extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _FacesItemWidgetState extends State<FacesItemWidget> {
|
||||
bool editMode = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -38,18 +36,14 @@ class _FacesItemWidgetState extends State<FacesItemWidget> {
|
||||
return InfoItemWidget(
|
||||
key: const ValueKey("Faces"),
|
||||
leadingIcon: Icons.face_retouching_natural_outlined,
|
||||
subtitleSection: _faceWidgets(context, widget.file, editMode),
|
||||
subtitleSection: _faceWidgets(context, widget.file),
|
||||
hasChipButtons: true,
|
||||
biggerSpinner: true,
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<Widget>> _faceWidgets(
|
||||
BuildContext context,
|
||||
EnteFile file,
|
||||
bool editMode,
|
||||
) async {
|
||||
late final mlDataDB = MLDataDB.instance;
|
||||
Future<List<Widget>> _faceWidgets(BuildContext context, EnteFile file) async {
|
||||
final mlDataDB = MLDataDB.instance;
|
||||
try {
|
||||
if (file.uploadedFileID == null) {
|
||||
return [const NoFaceChipButtonWidget(NoFacesReason.fileNotUploaded)];
|
||||
@@ -151,7 +145,6 @@ class _FacesItemWidgetState extends State<FacesItemWidget> {
|
||||
clusterID: clusterID,
|
||||
person: person,
|
||||
highlight: highlight,
|
||||
editMode: highlight ? editMode : false,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user