use 12/24 hour format system settings
This commit is contained in:
parent
07934bbf9e
commit
b94abc8be4
7 changed files with 137 additions and 116 deletions
|
@ -8,6 +8,7 @@ import android.database.Cursor
|
|||
import android.database.MatrixCursor
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.text.format.DateFormat
|
||||
import android.util.Log
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.utils.ContextUtils.resourceUri
|
||||
|
@ -83,6 +84,7 @@ class SearchSuggestionsProvider : MethodChannel.MethodCallHandler, ContentProvid
|
|||
backgroundChannel.invokeMethod("getSuggestions", hashMapOf(
|
||||
"query" to query,
|
||||
"locale" to Locale.getDefault().toString(),
|
||||
"use24hour" to DateFormat.is24HourFormat(context),
|
||||
), object : MethodChannel.Result {
|
||||
override fun success(result: Any?) {
|
||||
@Suppress("unchecked_cast")
|
||||
|
|
|
@ -47,6 +47,9 @@ Future<List<Map<String, String?>>> _getSuggestions(dynamic args) async {
|
|||
if (args is Map) {
|
||||
final query = args['query'];
|
||||
final locale = args['locale'];
|
||||
final use24hour = args['use24hour'];
|
||||
debugPrint('getSuggestions query=$query, locale=$locale use24hour=$use24hour');
|
||||
|
||||
if (query is String && locale is String) {
|
||||
final entries = await metadataDb.searchEntries(query, limit: 9);
|
||||
suggestions.addAll(entries.map((entry) {
|
||||
|
@ -55,7 +58,7 @@ Future<List<Map<String, String?>>> _getSuggestions(dynamic args) async {
|
|||
'data': entry.uri,
|
||||
'mimeType': entry.mimeType,
|
||||
'title': entry.bestTitle,
|
||||
'subtitle': date != null ? formatDateTime(date, locale) : null,
|
||||
'subtitle': date != null ? formatDateTime(date, locale, use24hour) : null,
|
||||
'iconUri': entry.uri,
|
||||
};
|
||||
}));
|
||||
|
|
|
@ -2,9 +2,9 @@ import 'package:intl/intl.dart';
|
|||
|
||||
String formatDay(DateTime date, String locale) => DateFormat.yMMMd(locale).format(date);
|
||||
|
||||
String formatTime(DateTime date, String locale) => DateFormat.Hm(locale).format(date);
|
||||
String formatTime(DateTime date, String locale, bool use24hour) => (use24hour ? DateFormat.Hm(locale) : DateFormat.jm(locale)).format(date);
|
||||
|
||||
String formatDateTime(DateTime date, String locale) => '${formatDay(date, locale)} • ${formatTime(date, locale)}';
|
||||
String formatDateTime(DateTime date, String locale, bool use24hour) => '${formatDay(date, locale)} • ${formatTime(date, locale, use24hour)}';
|
||||
|
||||
String formatFriendlyDuration(Duration d) {
|
||||
final seconds = (d.inSeconds.remainder(Duration.secondsPerMinute)).toString().padLeft(2, '0');
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:aves/theme/durations.dart';
|
|||
import 'package:aves/theme/format.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
@ -44,125 +45,134 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
void _updateAction(DateEditAction? action) {
|
||||
if (action == null) return;
|
||||
setState(() => _action = action);
|
||||
}
|
||||
return MediaQueryDataProvider(
|
||||
child: Builder(
|
||||
builder: (context) {
|
||||
final l10n = context.l10n;
|
||||
final locale = l10n.localeName;
|
||||
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
|
||||
|
||||
Widget _tileText(String text) => Text(
|
||||
text,
|
||||
softWrap: false,
|
||||
overflow: TextOverflow.fade,
|
||||
maxLines: 1,
|
||||
);
|
||||
void _updateAction(DateEditAction? action) {
|
||||
if (action == null) return;
|
||||
setState(() => _action = action);
|
||||
}
|
||||
|
||||
final setTile = Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RadioListTile<DateEditAction>(
|
||||
value: DateEditAction.set,
|
||||
Widget _tileText(String text) => Text(
|
||||
text,
|
||||
softWrap: false,
|
||||
overflow: TextOverflow.fade,
|
||||
maxLines: 1,
|
||||
);
|
||||
|
||||
final setTile = Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RadioListTile<DateEditAction>(
|
||||
value: DateEditAction.set,
|
||||
groupValue: _action,
|
||||
onChanged: _updateAction,
|
||||
title: _tileText(l10n.editEntryDateDialogSet),
|
||||
subtitle: Text(formatDateTime(_dateTime, locale, use24hour)),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 12),
|
||||
child: IconButton(
|
||||
icon: const Icon(AIcons.edit),
|
||||
onPressed: _action == DateEditAction.set ? _editDate : null,
|
||||
tooltip: l10n.changeTooltip,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
final shiftTile = Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RadioListTile<DateEditAction>(
|
||||
value: DateEditAction.shift,
|
||||
groupValue: _action,
|
||||
onChanged: _updateAction,
|
||||
title: _tileText(l10n.editEntryDateDialogShift),
|
||||
subtitle: Text(_formatShiftDuration()),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 12),
|
||||
child: IconButton(
|
||||
icon: const Icon(AIcons.edit),
|
||||
onPressed: _action == DateEditAction.shift ? _editShift : null,
|
||||
tooltip: l10n.changeTooltip,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
final clearTile = RadioListTile<DateEditAction>(
|
||||
value: DateEditAction.clear,
|
||||
groupValue: _action,
|
||||
onChanged: _updateAction,
|
||||
title: _tileText(l10n.editEntryDateDialogSet),
|
||||
subtitle: Text(formatDateTime(_dateTime, l10n.localeName)),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 12),
|
||||
child: IconButton(
|
||||
icon: const Icon(AIcons.edit),
|
||||
onPressed: _action == DateEditAction.set ? _editDate : null,
|
||||
tooltip: l10n.changeTooltip,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
final shiftTile = Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: RadioListTile<DateEditAction>(
|
||||
value: DateEditAction.shift,
|
||||
groupValue: _action,
|
||||
onChanged: _updateAction,
|
||||
title: _tileText(l10n.editEntryDateDialogShift),
|
||||
subtitle: Text(_formatShiftDuration()),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsetsDirectional.only(end: 12),
|
||||
child: IconButton(
|
||||
icon: const Icon(AIcons.edit),
|
||||
onPressed: _action == DateEditAction.shift ? _editShift : null,
|
||||
tooltip: l10n.changeTooltip,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
final clearTile = RadioListTile<DateEditAction>(
|
||||
value: DateEditAction.clear,
|
||||
groupValue: _action,
|
||||
onChanged: _updateAction,
|
||||
title: _tileText(l10n.editEntryDateDialogClear),
|
||||
);
|
||||
title: _tileText(l10n.editEntryDateDialogClear),
|
||||
);
|
||||
|
||||
final animationDuration = context.select<DurationsData, Duration>((v) => v.expansionTileAnimation);
|
||||
final theme = Theme.of(context);
|
||||
return Theme(
|
||||
data: theme.copyWith(
|
||||
textTheme: theme.textTheme.copyWith(
|
||||
// dense style font for tile subtitles, without modifying title font
|
||||
bodyText2: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
child: AvesDialog(
|
||||
context: context,
|
||||
title: l10n.editEntryDateDialogTitle,
|
||||
scrollableContent: [
|
||||
setTile,
|
||||
shiftTile,
|
||||
clearTile,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 1),
|
||||
child: ExpansionPanelList(
|
||||
expansionCallback: (index, isExpanded) {
|
||||
setState(() => _showOptions = !isExpanded);
|
||||
},
|
||||
animationDuration: animationDuration,
|
||||
expandedHeaderPadding: EdgeInsets.zero,
|
||||
elevation: 0,
|
||||
children: [
|
||||
ExpansionPanel(
|
||||
headerBuilder: (context, isExpanded) => ListTile(
|
||||
title: Text(l10n.editEntryDateDialogFieldSelection),
|
||||
final animationDuration = context.select<DurationsData, Duration>((v) => v.expansionTileAnimation);
|
||||
final theme = Theme.of(context);
|
||||
return Theme(
|
||||
data: theme.copyWith(
|
||||
textTheme: theme.textTheme.copyWith(
|
||||
// dense style font for tile subtitles, without modifying title font
|
||||
bodyText2: const TextStyle(fontSize: 12),
|
||||
),
|
||||
),
|
||||
child: AvesDialog(
|
||||
context: context,
|
||||
title: l10n.editEntryDateDialogTitle,
|
||||
scrollableContent: [
|
||||
setTile,
|
||||
shiftTile,
|
||||
clearTile,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 1),
|
||||
child: ExpansionPanelList(
|
||||
expansionCallback: (index, isExpanded) {
|
||||
setState(() => _showOptions = !isExpanded);
|
||||
},
|
||||
animationDuration: animationDuration,
|
||||
expandedHeaderPadding: EdgeInsets.zero,
|
||||
elevation: 0,
|
||||
children: [
|
||||
ExpansionPanel(
|
||||
headerBuilder: (context, isExpanded) => ListTile(
|
||||
title: Text(l10n.editEntryDateDialogFieldSelection),
|
||||
),
|
||||
body: Column(
|
||||
children: DateModifier.allDateFields
|
||||
.map((field) => SwitchListTile(
|
||||
value: _fields.contains(field),
|
||||
onChanged: (selected) => setState(() => selected ? _fields.add(field) : _fields.remove(field)),
|
||||
title: Text(_fieldTitle(field)),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
isExpanded: _showOptions,
|
||||
canTapOnHeader: true,
|
||||
backgroundColor: Theme.of(context).dialogBackgroundColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: DateModifier.allDateFields
|
||||
.map((field) => SwitchListTile(
|
||||
value: _fields.contains(field),
|
||||
onChanged: (selected) => setState(() => selected ? _fields.add(field) : _fields.remove(field)),
|
||||
title: Text(_fieldTitle(field)),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
isExpanded: _showOptions,
|
||||
canTapOnHeader: true,
|
||||
backgroundColor: Theme.of(context).dialogBackgroundColor,
|
||||
),
|
||||
],
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => _submit(context),
|
||||
child: Text(l10n.applyButtonLabel),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => _submit(context),
|
||||
child: Text(l10n.applyButtonLabel),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -168,8 +168,10 @@ class _DateRow extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final locale = context.l10n.localeName;
|
||||
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
|
||||
|
||||
final date = entry?.bestDate;
|
||||
final dateText = date != null ? formatDateTime(date, locale) : Constants.overlayUnknown;
|
||||
final dateText = date != null ? formatDateTime(date, locale, use24hour) : Constants.overlayUnknown;
|
||||
return Row(
|
||||
children: [
|
||||
const SizedBox(width: MapInfoRow.iconPadding),
|
||||
|
|
|
@ -17,6 +17,7 @@ import 'package:aves/widgets/viewer/info/common.dart';
|
|||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class BasicSection extends StatelessWidget {
|
||||
final AvesEntry entry;
|
||||
|
@ -41,6 +42,7 @@ class BasicSection extends StatelessWidget {
|
|||
final l10n = context.l10n;
|
||||
final infoUnknown = l10n.viewerInfoUnknown;
|
||||
final locale = l10n.localeName;
|
||||
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
|
||||
|
||||
return AnimatedBuilder(
|
||||
animation: entry.metadataChangeNotifier,
|
||||
|
@ -49,7 +51,7 @@ class BasicSection extends StatelessWidget {
|
|||
// inserting ZWSP (\u200B) between characters does help, but it messes with width and height computation (another Flutter issue)
|
||||
final title = entry.bestTitle ?? infoUnknown;
|
||||
final date = entry.bestDate;
|
||||
final dateText = date != null ? formatDateTime(date, locale) : infoUnknown;
|
||||
final dateText = date != null ? formatDateTime(date, locale, use24hour) : infoUnknown;
|
||||
final showResolution = !entry.isSvg && entry.isSized;
|
||||
final sizeText = entry.sizeBytes != null ? formatFilesize(entry.sizeBytes!) : infoUnknown;
|
||||
final path = entry.path;
|
||||
|
|
|
@ -395,8 +395,10 @@ class _DateRow extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final locale = context.l10n.localeName;
|
||||
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
|
||||
|
||||
final date = entry.bestDate;
|
||||
final dateText = date != null ? formatDateTime(date, locale) : Constants.overlayUnknown;
|
||||
final dateText = date != null ? formatDateTime(date, locale, use24hour) : Constants.overlayUnknown;
|
||||
final resolutionText = entry.isSvg
|
||||
? entry.aspectRatioText
|
||||
: entry.isSized
|
||||
|
|
Loading…
Reference in a new issue