mirror of
https://github.com/Myzel394/quid_faciam_hodie.git
synced 2025-06-19 15:45:26 +02:00
added annotations; improvements & bugfixes
This commit is contained in:
parent
9f37648762
commit
5a8a0352c1
@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||||
import 'package:fluttertoast/fluttertoast.dart';
|
import 'package:fluttertoast/fluttertoast.dart';
|
||||||
import 'package:quid_faciam_hodie/constants/values.dart';
|
import 'package:quid_faciam_hodie/constants/values.dart';
|
||||||
import 'package:supabase_flutter/supabase_flutter.dart';
|
import 'package:supabase_flutter/supabase_flutter.dart';
|
||||||
@ -9,12 +10,17 @@ extension ShowSnackBar on BuildContext {
|
|||||||
static ScaffoldFeatureController<SnackBar, SnackBarClosedReason>?
|
static ScaffoldFeatureController<SnackBar, SnackBarClosedReason>?
|
||||||
pendingSnackBar;
|
pendingSnackBar;
|
||||||
|
|
||||||
ScaffoldFeatureController<SnackBar, SnackBarClosedReason> showSnackBar({
|
ScaffoldFeatureController<SnackBar, SnackBarClosedReason>? showSnackBar({
|
||||||
required final String message,
|
required final String message,
|
||||||
final Color backgroundColor = Colors.white,
|
final Color backgroundColor = Colors.white,
|
||||||
final Duration duration = const Duration(seconds: 4),
|
final Duration duration = const Duration(seconds: 4),
|
||||||
final BuildContext? context,
|
final BuildContext? context,
|
||||||
}) {
|
}) {
|
||||||
|
if (!isMaterial(context ?? this)) {
|
||||||
|
// Not implemented yet
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
pendingSnackBar?.close();
|
pendingSnackBar?.close();
|
||||||
pendingSnackBar = null;
|
pendingSnackBar = null;
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ class Memory {
|
|||||||
final String filePath;
|
final String filePath;
|
||||||
final bool isPublic;
|
final bool isPublic;
|
||||||
final String userID;
|
final String userID;
|
||||||
|
final String annotation;
|
||||||
final MemoryLocation? location;
|
final MemoryLocation? location;
|
||||||
|
|
||||||
const Memory({
|
const Memory({
|
||||||
@ -20,6 +21,7 @@ class Memory {
|
|||||||
required this.filePath,
|
required this.filePath,
|
||||||
required this.isPublic,
|
required this.isPublic,
|
||||||
required this.userID,
|
required this.userID,
|
||||||
|
required this.annotation,
|
||||||
this.location,
|
this.location,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -29,6 +31,7 @@ class Memory {
|
|||||||
filePath: jsonData['location'],
|
filePath: jsonData['location'],
|
||||||
isPublic: jsonData['is_public'],
|
isPublic: jsonData['is_public'],
|
||||||
userID: jsonData['user_id'],
|
userID: jsonData['user_id'],
|
||||||
|
annotation: jsonData['annotation'],
|
||||||
location: MemoryLocation.parse(jsonData),
|
location: MemoryLocation.parse(jsonData),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"generalError": "There was an error",
|
"generalError": "There was an error",
|
||||||
"generalCancelButtonLabel": "Cancel",
|
"generalCancelButtonLabel": "Cancel",
|
||||||
"generalContinueButtonLabel": "Continue",
|
"generalContinueButtonLabel": "Continue",
|
||||||
|
"generalSaveButtonLabel": "Save",
|
||||||
"generalUnderstoodButtonLabel": "OK",
|
"generalUnderstoodButtonLabel": "OK",
|
||||||
"generalLoadingLabel": "Loading...",
|
"generalLoadingLabel": "Loading...",
|
||||||
|
|
||||||
@ -52,6 +53,10 @@
|
|||||||
"mainScreenHelpSheetTakePhotoExplanation": "Tap on the shutter button once to take a photo.",
|
"mainScreenHelpSheetTakePhotoExplanation": "Tap on the shutter button once to take a photo.",
|
||||||
"mainScreenHelpSheetTakeVideoExplanation": "Hold down the shutter button to start recording a video. Leave the button to stop recording.",
|
"mainScreenHelpSheetTakeVideoExplanation": "Hold down the shutter button to start recording a video. Leave the button to stop recording.",
|
||||||
|
|
||||||
|
"mainScreenAnnotationDialogTitle": "Add an annotation",
|
||||||
|
"mainScreenAnnotationDialogExplanation": "You can add an annotation to your memory",
|
||||||
|
"mainScreenAnnotationDialogAnnotationFieldLabel": "Annotation",
|
||||||
|
|
||||||
"recordingOverlayIsRecording": "Recording",
|
"recordingOverlayIsRecording": "Recording",
|
||||||
|
|
||||||
|
|
||||||
@ -151,6 +156,7 @@
|
|||||||
"settingsScreenDeleteAccountConfirmLabel": "Delete Account now",
|
"settingsScreenDeleteAccountConfirmLabel": "Delete Account now",
|
||||||
"settingsScreenGeneralSectionTitle": "General",
|
"settingsScreenGeneralSectionTitle": "General",
|
||||||
"settingsScreenGeneralSectionQualityLabel": "Quality",
|
"settingsScreenGeneralSectionQualityLabel": "Quality",
|
||||||
|
"settingsScreenGeneralSectionAskForMemoryAnnotationsLabel": "Ask for memory annotations",
|
||||||
"settingsScreenResetHelpSheetsLabel": "Reset Help Sheets",
|
"settingsScreenResetHelpSheetsLabel": "Reset Help Sheets",
|
||||||
"settingsScreenResetHelpSheetsResetSuccessfully": "Help Sheets reset successfully.",
|
"settingsScreenResetHelpSheetsResetSuccessfully": "Help Sheets reset successfully.",
|
||||||
|
|
||||||
|
@ -36,7 +36,8 @@ class FileManager {
|
|||||||
static uploadFile(
|
static uploadFile(
|
||||||
final User user,
|
final User user,
|
||||||
final File file, {
|
final File file, {
|
||||||
LocationData? locationData,
|
final LocationData? locationData,
|
||||||
|
final Future<String?>? annotationGetterFuture,
|
||||||
}) async {
|
}) async {
|
||||||
await GlobalValuesManager.waitForInitialization();
|
await GlobalValuesManager.waitForInitialization();
|
||||||
|
|
||||||
@ -65,6 +66,15 @@ class FileManager {
|
|||||||
data['location_heading'] = locationData.heading!;
|
data['location_heading'] = locationData.heading!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (annotationGetterFuture != null) {
|
||||||
|
final annotation = await annotationGetterFuture;
|
||||||
|
|
||||||
|
if (annotation != null) {
|
||||||
|
// User has specified annotation
|
||||||
|
data['annotation'] = annotation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final memoryResponse =
|
final memoryResponse =
|
||||||
await supabase.from('memories').insert(data).execute();
|
await supabase.from('memories').insert(data).execute();
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:camera/camera.dart';
|
import 'package:camera/camera.dart';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||||
import 'package:quid_faciam_hodie/constants/storage_keys.dart';
|
import 'package:quid_faciam_hodie/constants/storage_keys.dart';
|
||||||
@ -9,14 +10,20 @@ const secure = FlutterSecureStorage();
|
|||||||
|
|
||||||
class Settings extends ChangeNotifier {
|
class Settings extends ChangeNotifier {
|
||||||
ResolutionPreset _resolution = ResolutionPreset.max;
|
ResolutionPreset _resolution = ResolutionPreset.max;
|
||||||
|
bool _askForMemoryAnnotations = false;
|
||||||
|
|
||||||
Settings({final ResolutionPreset resolution = ResolutionPreset.max})
|
Settings({
|
||||||
: _resolution = resolution;
|
final ResolutionPreset? resolution,
|
||||||
|
final bool? askForMemoryAnnotations,
|
||||||
|
}) : _resolution = resolution ?? ResolutionPreset.max,
|
||||||
|
_askForMemoryAnnotations = askForMemoryAnnotations ?? true;
|
||||||
|
|
||||||
ResolutionPreset get resolution => _resolution;
|
ResolutionPreset get resolution => _resolution;
|
||||||
|
bool get askForMemoryAnnotations => _askForMemoryAnnotations;
|
||||||
|
|
||||||
Map<String, dynamic> toJSONData() => {
|
Map<String, dynamic> toJSONData() => {
|
||||||
'resolution': _resolution.toString(),
|
'resolution': _resolution.toString(),
|
||||||
|
'askForMemoryAnnotations': _askForMemoryAnnotations ? 'true' : 'false',
|
||||||
};
|
};
|
||||||
|
|
||||||
Future<void> save() async {
|
Future<void> save() async {
|
||||||
@ -36,11 +43,23 @@ class Settings extends ChangeNotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final data = jsonDecode(rawData);
|
final data = jsonDecode(rawData);
|
||||||
final resolution = ResolutionPreset.values.firstWhere(
|
final resolution = ResolutionPreset.values.firstWhereOrNull(
|
||||||
(preset) => preset.toString() == data['resolution'],
|
(preset) => preset.toString() == data['resolution'],
|
||||||
);
|
);
|
||||||
|
final askForMemoryAnnotations = () {
|
||||||
|
switch (data['askForMemoryAnnotations']) {
|
||||||
|
case 'true':
|
||||||
|
return true;
|
||||||
|
case 'false':
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
return Settings(
|
return Settings(
|
||||||
resolution: resolution,
|
resolution: resolution,
|
||||||
|
askForMemoryAnnotations: askForMemoryAnnotations,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,4 +68,10 @@ class Settings extends ChangeNotifier {
|
|||||||
notifyListeners();
|
notifyListeners();
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setAskForMemoryAnnotations(final bool askForMemoryAnnotations) {
|
||||||
|
_askForMemoryAnnotations = askForMemoryAnnotations;
|
||||||
|
notifyListeners();
|
||||||
|
save();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,10 +82,9 @@ class _LoginScreenState extends AuthState<LoginScreen> with Loadable {
|
|||||||
await _signUp();
|
await _signUp();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
if (isMaterial(context))
|
context.showLongErrorSnackBar(
|
||||||
context.showLongErrorSnackBar(
|
message: localizations.loginScreenLoginFailed,
|
||||||
message: localizations.loginScreenLoginFailed,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
passwordController.clear();
|
passwordController.clear();
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import 'package:quid_faciam_hodie/constants/values.dart';
|
|||||||
import 'package:quid_faciam_hodie/extensions/snackbar.dart';
|
import 'package:quid_faciam_hodie/extensions/snackbar.dart';
|
||||||
import 'package:quid_faciam_hodie/managers/file_manager.dart';
|
import 'package:quid_faciam_hodie/managers/file_manager.dart';
|
||||||
import 'package:quid_faciam_hodie/managers/global_values_manager.dart';
|
import 'package:quid_faciam_hodie/managers/global_values_manager.dart';
|
||||||
|
import 'package:quid_faciam_hodie/screens/main_screen/annotation_dialog.dart';
|
||||||
import 'package:quid_faciam_hodie/screens/main_screen/camera_help_content.dart';
|
import 'package:quid_faciam_hodie/screens/main_screen/camera_help_content.dart';
|
||||||
import 'package:quid_faciam_hodie/screens/main_screen/settings_button_overlay.dart';
|
import 'package:quid_faciam_hodie/screens/main_screen/settings_button_overlay.dart';
|
||||||
import 'package:quid_faciam_hodie/utils/auth_required.dart';
|
import 'package:quid_faciam_hodie/utils/auth_required.dart';
|
||||||
@ -188,6 +189,62 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<String?> _createAskAnnotationDialog() => showPlatformDialog(
|
||||||
|
barrierDismissible: true,
|
||||||
|
context: context,
|
||||||
|
builder: (dialogContext) => const AnnotationDialog(),
|
||||||
|
);
|
||||||
|
|
||||||
|
void _lockCamera() => setState(() {
|
||||||
|
lockCamera = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
void _releaseCamera() => setState(() {
|
||||||
|
lockCamera = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
void _showUploadingPhotoAnimation(final File file) => setState(() {
|
||||||
|
uploadingPhotoAnimation = file.readAsBytesSync();
|
||||||
|
});
|
||||||
|
|
||||||
|
void _releaseUploadingPhotoAnimation() => setState(() {
|
||||||
|
uploadingPhotoAnimation = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<String?> getAnnotation() async {
|
||||||
|
final settings = GlobalValuesManager.settings!;
|
||||||
|
|
||||||
|
if (settings.askForMemoryAnnotations) {
|
||||||
|
return _createAskAnnotationDialog();
|
||||||
|
} else {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> setFlashModeBeforeApplyingAction() async {
|
||||||
|
if (isTorchEnabled) {
|
||||||
|
await controller!.setFlashMode(FlashMode.torch);
|
||||||
|
} else {
|
||||||
|
await controller!.setFlashMode(FlashMode.off);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<LocationData?> getLocation([
|
||||||
|
final File? fileToTag,
|
||||||
|
]) async {
|
||||||
|
if (!(await Permission.location.isGranted)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final locationData = await Location().getLocation();
|
||||||
|
|
||||||
|
if (fileToTag != null && Platform.isAndroid) {
|
||||||
|
await tagLocationToImage(fileToTag, locationData);
|
||||||
|
}
|
||||||
|
|
||||||
|
return locationData;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> takePhoto() async {
|
Future<void> takePhoto() async {
|
||||||
final localizations = AppLocalizations.of(context)!;
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
@ -195,109 +252,94 @@ class _MainScreenState extends AuthRequiredState<MainScreen> with Loadable {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
_lockCamera();
|
||||||
lockCamera = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isMaterial(context))
|
context.showPendingSnackBar(
|
||||||
context.showPendingSnackBar(
|
message: localizations.mainScreenTakePhotoActionTakingPhoto,
|
||||||
message: localizations.mainScreenTakePhotoActionTakingPhoto,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if (isTorchEnabled) {
|
await setFlashModeBeforeApplyingAction();
|
||||||
await controller!.setFlashMode(FlashMode.torch);
|
|
||||||
} else {
|
|
||||||
await controller!.setFlashMode(FlashMode.off);
|
|
||||||
}
|
|
||||||
|
|
||||||
final file = File((await controller!.takePicture()).path);
|
final file = File((await controller!.takePicture()).path);
|
||||||
|
|
||||||
setState(() {
|
final annotationGetterFuture = getAnnotation();
|
||||||
uploadingPhotoAnimation = file.readAsBytesSync();
|
final locationData = await getLocation(file);
|
||||||
});
|
|
||||||
|
|
||||||
if (isMaterial(context))
|
_showUploadingPhotoAnimation(file);
|
||||||
context.showPendingSnackBar(
|
|
||||||
message: localizations.mainScreenTakePhotoActionUploadingPhoto,
|
|
||||||
);
|
|
||||||
|
|
||||||
LocationData? locationData;
|
context.showPendingSnackBar(
|
||||||
|
message: localizations.mainScreenTakePhotoActionUploadingPhoto,
|
||||||
if (await Permission.location.isGranted) {
|
);
|
||||||
locationData = await Location().getLocation();
|
|
||||||
|
|
||||||
if (Platform.isAndroid) {
|
|
||||||
await tagLocationToImage(file, locationData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await FileManager.uploadFile(_user, file, locationData: locationData);
|
await FileManager.uploadFile(
|
||||||
|
_user,
|
||||||
|
file,
|
||||||
|
locationData: locationData,
|
||||||
|
annotationGetterFuture: annotationGetterFuture,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isMaterial(context))
|
context.showErrorSnackBar(message: error.toString());
|
||||||
context.showErrorSnackBar(message: error.toString());
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMaterial(context))
|
context.showSuccessSnackBar(
|
||||||
context.showSuccessSnackBar(
|
message: localizations.mainScreenUploadSuccess,
|
||||||
message: localizations.mainScreenUploadSuccess,
|
);
|
||||||
);
|
|
||||||
} finally {
|
} finally {
|
||||||
setState(() {
|
_releaseCamera();
|
||||||
lockCamera = false;
|
_releaseUploadingPhotoAnimation();
|
||||||
uploadingPhotoAnimation = null;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> takeVideo() async {
|
Future<void> takeVideo() async {
|
||||||
final localizations = AppLocalizations.of(context)!;
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
setState(() {
|
|
||||||
isRecording = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!controller!.value.isRecordingVideo) {
|
if (!controller!.value.isRecordingVideo) {
|
||||||
// Recording has already been stopped
|
// Recording has already been stopped
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
lockCamera = true;
|
isRecording = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_lockCamera();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (isMaterial(context))
|
context.showPendingSnackBar(
|
||||||
context.showPendingSnackBar(
|
message: localizations.mainScreenTakeVideoActionSaveVideo,
|
||||||
message: localizations.mainScreenTakeVideoActionSaveVideo,
|
);
|
||||||
);
|
|
||||||
|
|
||||||
final file = File((await controller!.stopVideoRecording()).path);
|
final file = File((await controller!.stopVideoRecording()).path);
|
||||||
|
|
||||||
if (isMaterial(context))
|
final annotationGetterFuture = getAnnotation();
|
||||||
context.showPendingSnackBar(
|
final locationData = await getLocation();
|
||||||
message: localizations.mainScreenTakeVideoActionUploadingVideo,
|
|
||||||
);
|
context.showPendingSnackBar(
|
||||||
|
message: localizations.mainScreenTakeVideoActionUploadingVideo,
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await FileManager.uploadFile(_user, file);
|
await FileManager.uploadFile(
|
||||||
|
_user,
|
||||||
|
file,
|
||||||
|
annotationGetterFuture: annotationGetterFuture,
|
||||||
|
locationData: locationData,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isMaterial(context)) {
|
context.showErrorSnackBar(message: error.toString());
|
||||||
context.showErrorSnackBar(message: error.toString());
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMaterial(context))
|
context.showSuccessSnackBar(
|
||||||
context.showSuccessSnackBar(
|
message: localizations.mainScreenUploadSuccess,
|
||||||
message: localizations.mainScreenUploadSuccess,
|
);
|
||||||
);
|
|
||||||
} finally {
|
} finally {
|
||||||
setState(() {
|
_releaseCamera();
|
||||||
lockCamera = false;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
74
lib/screens/main_screen/annotation_dialog.dart
Normal file
74
lib/screens/main_screen/annotation_dialog.dart
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||||
|
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
||||||
|
import 'package:quid_faciam_hodie/utils/theme.dart';
|
||||||
|
|
||||||
|
class AnnotationDialog extends StatefulWidget {
|
||||||
|
const AnnotationDialog({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<AnnotationDialog> createState() => _AnnotationDialogState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AnnotationDialogState extends State<AnnotationDialog> {
|
||||||
|
final TextEditingController controller = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
controller.dispose();
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
|
return PlatformAlertDialog(
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(MEDIUM_SPACE),
|
||||||
|
child: Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Text(
|
||||||
|
localizations.mainScreenAnnotationDialogTitle,
|
||||||
|
style: getTitleTextStyle(context),
|
||||||
|
),
|
||||||
|
const SizedBox(height: MEDIUM_SPACE),
|
||||||
|
Text(
|
||||||
|
localizations.mainScreenAnnotationDialogExplanation,
|
||||||
|
style: getBodyTextTextStyle(context),
|
||||||
|
),
|
||||||
|
const SizedBox(height: MEDIUM_SPACE),
|
||||||
|
TextField(
|
||||||
|
controller: controller,
|
||||||
|
autofocus: true,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: localizations
|
||||||
|
.mainScreenAnnotationDialogAnnotationFieldLabel,
|
||||||
|
),
|
||||||
|
onSubmitted: (value) {
|
||||||
|
Navigator.of(context).pop(value);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
PlatformDialogAction(
|
||||||
|
child: Text(localizations.generalCancelButtonLabel),
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
),
|
||||||
|
PlatformDialogAction(
|
||||||
|
child: Text(localizations.generalSaveButtonLabel),
|
||||||
|
onPressed: () => Navigator.pop(context, controller.text.trim()),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -83,7 +83,7 @@ class _SettingsScreenState extends AuthRequiredState<SettingsScreen>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget getPicker() {
|
Widget getQualityPicker() {
|
||||||
final settings = GlobalValuesManager.settings!;
|
final settings = GlobalValuesManager.settings!;
|
||||||
final resolutionTextMapping = getResolutionTextMapping(context);
|
final resolutionTextMapping = getResolutionTextMapping(context);
|
||||||
final items = ResolutionPreset.values
|
final items = ResolutionPreset.values
|
||||||
@ -125,6 +125,7 @@ class _SettingsScreenState extends AuthRequiredState<SettingsScreen>
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final settings = GlobalValuesManager.settings!;
|
||||||
final localizations = AppLocalizations.of(context)!;
|
final localizations = AppLocalizations.of(context)!;
|
||||||
|
|
||||||
return PlatformScaffold(
|
return PlatformScaffold(
|
||||||
@ -203,7 +204,15 @@ class _SettingsScreenState extends AuthRequiredState<SettingsScreen>
|
|||||||
localizations
|
localizations
|
||||||
.settingsScreenGeneralSectionQualityLabel,
|
.settingsScreenGeneralSectionQualityLabel,
|
||||||
),
|
),
|
||||||
title: getPicker(),
|
title: getQualityPicker(),
|
||||||
|
),
|
||||||
|
SettingsTile.switchTile(
|
||||||
|
initialValue: settings.askForMemoryAnnotations,
|
||||||
|
onToggle: settings.setAskForMemoryAnnotations,
|
||||||
|
title: Text(
|
||||||
|
localizations
|
||||||
|
.settingsScreenGeneralSectionAskForMemoryAnnotationsLabel,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
SettingsTile(
|
SettingsTile(
|
||||||
leading: Icon(context.platformIcons.help),
|
leading: Icon(context.platformIcons.help),
|
||||||
@ -213,12 +222,10 @@ class _SettingsScreenState extends AuthRequiredState<SettingsScreen>
|
|||||||
onPressed: (_) async {
|
onPressed: (_) async {
|
||||||
await UserHelpSheetsManager.deleteAll();
|
await UserHelpSheetsManager.deleteAll();
|
||||||
|
|
||||||
if (isMaterial(context)) {
|
context.showSuccessSnackBar(
|
||||||
context.showSuccessSnackBar(
|
message: localizations
|
||||||
message: localizations
|
.settingsScreenResetHelpSheetsResetSuccessfully,
|
||||||
.settingsScreenResetHelpSheetsResetSuccessfully,
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@ -65,13 +65,11 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
|
|||||||
|
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
||||||
if (isMaterial(context))
|
context.showSuccessSnackBar(
|
||||||
context.showSuccessSnackBar(
|
message: localizations.memorySheetSavedToGallery,
|
||||||
message: localizations.memorySheetSavedToGallery,
|
);
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isMaterial(context))
|
context.showErrorSnackBar(message: localizations.generalError);
|
||||||
context.showErrorSnackBar(message: localizations.generalError);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,10 +92,9 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
|
|||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
|
|
||||||
if (isNowPublic) {
|
if (isNowPublic) {
|
||||||
if (isMaterial(context))
|
context.showSuccessSnackBar(
|
||||||
context.showSuccessSnackBar(
|
message: localizations.memorySheetMemoryUpdatedToPublic,
|
||||||
message: localizations.memorySheetMemoryUpdatedToPublic,
|
);
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
if (isMaterial(context))
|
if (isMaterial(context))
|
||||||
context.showSuccessSnackBar(
|
context.showSuccessSnackBar(
|
||||||
@ -105,8 +102,7 @@ class _MemorySheetState extends State<MemorySheet> with Loadable {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (isMaterial(context))
|
context.showErrorSnackBar(message: localizations.generalError);
|
||||||
context.showErrorSnackBar(message: localizations.generalError);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
import 'package:flutter_platform_widgets/flutter_platform_widgets.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
import 'package:quid_faciam_hodie/constants/spacing.dart';
|
||||||
import 'package:quid_faciam_hodie/enums.dart';
|
import 'package:quid_faciam_hodie/enums.dart';
|
||||||
import 'package:quid_faciam_hodie/foreign_types/memory.dart';
|
import 'package:quid_faciam_hodie/foreign_types/memory.dart';
|
||||||
|
import 'package:quid_faciam_hodie/models/timeline.dart';
|
||||||
import 'package:quid_faciam_hodie/widgets/raw_memory_display.dart';
|
import 'package:quid_faciam_hodie/widgets/raw_memory_display.dart';
|
||||||
import 'package:video_player/video_player.dart';
|
import 'package:video_player/video_player.dart';
|
||||||
|
|
||||||
@ -37,6 +40,7 @@ class MemoryView extends StatefulWidget {
|
|||||||
class _MemoryViewState extends State<MemoryView> {
|
class _MemoryViewState extends State<MemoryView> {
|
||||||
MemoryFetchStatus status = MemoryFetchStatus.downloading;
|
MemoryFetchStatus status = MemoryFetchStatus.downloading;
|
||||||
Uint8List? data;
|
Uint8List? data;
|
||||||
|
Timer? _nextMemoryTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -45,7 +49,16 @@ class _MemoryViewState extends State<MemoryView> {
|
|||||||
loadMemoryFile();
|
loadMemoryFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_nextMemoryTimer?.cancel();
|
||||||
|
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> loadMemoryFile() async {
|
Future<void> loadMemoryFile() async {
|
||||||
|
final timeline = context.read<TimelineModel>();
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
status = MemoryFetchStatus.downloading;
|
status = MemoryFetchStatus.downloading;
|
||||||
});
|
});
|
||||||
@ -75,6 +88,12 @@ class _MemoryViewState extends State<MemoryView> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
status = MemoryFetchStatus.error;
|
status = MemoryFetchStatus.error;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_nextMemoryTimer = Timer(
|
||||||
|
const Duration(seconds: 1),
|
||||||
|
timeline.nextMemory,
|
||||||
|
);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,44 +51,70 @@ class TimelineOverlay extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
right: SMALL_SPACE,
|
left: 0,
|
||||||
|
right: 0,
|
||||||
bottom: SMALL_SPACE * 2,
|
bottom: SMALL_SPACE * 2,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: SMALL_SPACE),
|
padding: const EdgeInsets.symmetric(horizontal: MEDIUM_SPACE),
|
||||||
child: AnimatedOpacity(
|
child: AnimatedOpacity(
|
||||||
duration: const Duration(milliseconds: 500),
|
duration: const Duration(milliseconds: 500),
|
||||||
curve: Curves.linearToEaseOut,
|
curve: Curves.linearToEaseOut,
|
||||||
opacity: timeline.showOverlay ? 1.0 : 0.0,
|
opacity: timeline.showOverlay ? 1.0 : 0.0,
|
||||||
child: Row(
|
child: Row(
|
||||||
|
mainAxisAlignment:
|
||||||
|
(timeline.currentMemory.annotation.isNotEmpty)
|
||||||
|
? MainAxisAlignment.spaceBetween
|
||||||
|
: MainAxisAlignment.end,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
AnimatedOpacity(
|
if (timeline.currentMemory.annotation.isNotEmpty)
|
||||||
opacity: timeline.currentMemory.isPublic ? 1.0 : 0.0,
|
Text(
|
||||||
duration: const Duration(milliseconds: 500),
|
timeline.currentMemory.annotation,
|
||||||
curve: Curves.linearToEaseOut,
|
style: platformThemeData(
|
||||||
child: Icon(
|
|
||||||
Icons.public,
|
|
||||||
size: platformThemeData(
|
|
||||||
context,
|
context,
|
||||||
material: (data) => data.textTheme.bodyLarge!.fontSize,
|
material: (data) => data.textTheme.titleSmall!.copyWith(
|
||||||
cupertino: (data) => data.textTheme.textStyle.fontSize,
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
cupertino: (data) =>
|
||||||
|
data.textTheme.navTitleTextStyle.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
color: Colors.white,
|
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
children: <Widget>[
|
||||||
|
AnimatedOpacity(
|
||||||
|
opacity: timeline.currentMemory.isPublic ? 1.0 : 0.0,
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
curve: Curves.linearToEaseOut,
|
||||||
|
child: Icon(
|
||||||
|
Icons.public,
|
||||||
|
size: platformThemeData(
|
||||||
|
context,
|
||||||
|
material: (data) =>
|
||||||
|
data.textTheme.bodyLarge!.fontSize,
|
||||||
|
cupertino: (data) =>
|
||||||
|
data.textTheme.textStyle.fontSize,
|
||||||
|
),
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: SMALL_SPACE),
|
||||||
|
Text(
|
||||||
|
'$memoryIndex/$memoriesAmount',
|
||||||
|
style: platformThemeData(
|
||||||
|
context,
|
||||||
|
material: (data) =>
|
||||||
|
data.textTheme.titleSmall!.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
cupertino: (data) =>
|
||||||
|
data.textTheme.navTitleTextStyle.copyWith(
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(width: SMALL_SPACE),
|
|
||||||
Text(
|
|
||||||
'$memoryIndex/$memoriesAmount',
|
|
||||||
style: platformThemeData(
|
|
||||||
context,
|
|
||||||
material: (data) => data.textTheme.titleSmall!.copyWith(
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
cupertino: (data) =>
|
|
||||||
data.textTheme.navTitleTextStyle.copyWith(
|
|
||||||
color: Colors.white,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -60,11 +60,9 @@ class KeyValueInfo extends StatelessWidget {
|
|||||||
HapticFeedback.lightImpact();
|
HapticFeedback.lightImpact();
|
||||||
Clipboard.setData(ClipboardData(text: value));
|
Clipboard.setData(ClipboardData(text: value));
|
||||||
|
|
||||||
if (isMaterial(context)) {
|
context.showSuccessSnackBar(
|
||||||
context.showSuccessSnackBar(
|
message: 'Copied to clipboard!',
|
||||||
message: 'Copied to clipboard!',
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
|
@ -43,12 +43,18 @@ class _RawMemoryDisplayState extends State<RawMemoryDisplay> {
|
|||||||
Future<File> createTempVideo() async {
|
Future<File> createTempVideo() async {
|
||||||
final tempDirectory = await getTemporaryDirectory();
|
final tempDirectory = await getTemporaryDirectory();
|
||||||
final path = '${tempDirectory.path}/${widget.filename ?? 'video.mp4'}';
|
final path = '${tempDirectory.path}/${widget.filename ?? 'video.mp4'}';
|
||||||
|
print("#" * 50);
|
||||||
|
print(widget.filename);
|
||||||
|
print(path);
|
||||||
final file = File(path);
|
final file = File(path);
|
||||||
|
print(await file.exists());
|
||||||
|
print(widget.data);
|
||||||
|
|
||||||
|
/*
|
||||||
if (await file.exists()) {
|
if (await file.exists()) {
|
||||||
// File already exists, so just return it
|
// File already exists, so just return it
|
||||||
return file;
|
return file;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
// File needs to be created
|
// File needs to be created
|
||||||
await file.create();
|
await file.create();
|
||||||
|
@ -441,6 +441,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.2"
|
version: "1.0.2"
|
||||||
|
morphing_text:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: morphing_text
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.1"
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -63,6 +63,7 @@ dependencies:
|
|||||||
flutter_osm_plugin: ^0.39.0
|
flutter_osm_plugin: ^0.39.0
|
||||||
url_launcher: ^6.1.5
|
url_launcher: ^6.1.5
|
||||||
apple_maps_flutter: ^1.2.0
|
apple_maps_flutter: ^1.2.0
|
||||||
|
morphing_text: ^1.0.1
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user