use 12/24 hour format system settings

This commit is contained in:
Thibault Deckers 2021-10-28 09:45:37 +09:00
parent 07934bbf9e
commit b94abc8be4
7 changed files with 137 additions and 116 deletions

View file

@ -8,6 +8,7 @@ import android.database.Cursor
import android.database.MatrixCursor import android.database.MatrixCursor
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.text.format.DateFormat
import android.util.Log import android.util.Log
import deckers.thibault.aves.model.FieldMap import deckers.thibault.aves.model.FieldMap
import deckers.thibault.aves.utils.ContextUtils.resourceUri import deckers.thibault.aves.utils.ContextUtils.resourceUri
@ -83,6 +84,7 @@ class SearchSuggestionsProvider : MethodChannel.MethodCallHandler, ContentProvid
backgroundChannel.invokeMethod("getSuggestions", hashMapOf( backgroundChannel.invokeMethod("getSuggestions", hashMapOf(
"query" to query, "query" to query,
"locale" to Locale.getDefault().toString(), "locale" to Locale.getDefault().toString(),
"use24hour" to DateFormat.is24HourFormat(context),
), object : MethodChannel.Result { ), object : MethodChannel.Result {
override fun success(result: Any?) { override fun success(result: Any?) {
@Suppress("unchecked_cast") @Suppress("unchecked_cast")

View file

@ -47,6 +47,9 @@ Future<List<Map<String, String?>>> _getSuggestions(dynamic args) async {
if (args is Map) { if (args is Map) {
final query = args['query']; final query = args['query'];
final locale = args['locale']; final locale = args['locale'];
final use24hour = args['use24hour'];
debugPrint('getSuggestions query=$query, locale=$locale use24hour=$use24hour');
if (query is String && locale is String) { if (query is String && locale is String) {
final entries = await metadataDb.searchEntries(query, limit: 9); final entries = await metadataDb.searchEntries(query, limit: 9);
suggestions.addAll(entries.map((entry) { suggestions.addAll(entries.map((entry) {
@ -55,7 +58,7 @@ Future<List<Map<String, String?>>> _getSuggestions(dynamic args) async {
'data': entry.uri, 'data': entry.uri,
'mimeType': entry.mimeType, 'mimeType': entry.mimeType,
'title': entry.bestTitle, 'title': entry.bestTitle,
'subtitle': date != null ? formatDateTime(date, locale) : null, 'subtitle': date != null ? formatDateTime(date, locale, use24hour) : null,
'iconUri': entry.uri, 'iconUri': entry.uri,
}; };
})); }));

View file

@ -2,9 +2,9 @@ import 'package:intl/intl.dart';
String formatDay(DateTime date, String locale) => DateFormat.yMMMd(locale).format(date); 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) { String formatFriendlyDuration(Duration d) {
final seconds = (d.inSeconds.remainder(Duration.secondsPerMinute)).toString().padLeft(2, '0'); final seconds = (d.inSeconds.remainder(Duration.secondsPerMinute)).toString().padLeft(2, '0');

View file

@ -5,6 +5,7 @@ import 'package:aves/theme/durations.dart';
import 'package:aves/theme/format.dart'; import 'package:aves/theme/format.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.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:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
@ -44,125 +45,134 @@ class _EditEntryDateDialogState extends State<EditEntryDateDialog> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final l10n = context.l10n; return MediaQueryDataProvider(
void _updateAction(DateEditAction? action) { child: Builder(
if (action == null) return; builder: (context) {
setState(() => _action = action); final l10n = context.l10n;
} final locale = l10n.localeName;
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
Widget _tileText(String text) => Text( void _updateAction(DateEditAction? action) {
text, if (action == null) return;
softWrap: false, setState(() => _action = action);
overflow: TextOverflow.fade, }
maxLines: 1,
);
final setTile = Row( Widget _tileText(String text) => Text(
children: [ text,
Expanded( softWrap: false,
child: RadioListTile<DateEditAction>( overflow: TextOverflow.fade,
value: DateEditAction.set, 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, groupValue: _action,
onChanged: _updateAction, onChanged: _updateAction,
title: _tileText(l10n.editEntryDateDialogSet), title: _tileText(l10n.editEntryDateDialogClear),
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),
);
final animationDuration = context.select<DurationsData, Duration>((v) => v.expansionTileAnimation); final animationDuration = context.select<DurationsData, Duration>((v) => v.expansionTileAnimation);
final theme = Theme.of(context); final theme = Theme.of(context);
return Theme( return Theme(
data: theme.copyWith( data: theme.copyWith(
textTheme: theme.textTheme.copyWith( textTheme: theme.textTheme.copyWith(
// dense style font for tile subtitles, without modifying title font // dense style font for tile subtitles, without modifying title font
bodyText2: const TextStyle(fontSize: 12), bodyText2: const TextStyle(fontSize: 12),
), ),
), ),
child: AvesDialog( child: AvesDialog(
context: context, context: context,
title: l10n.editEntryDateDialogTitle, title: l10n.editEntryDateDialogTitle,
scrollableContent: [ scrollableContent: [
setTile, setTile,
shiftTile, shiftTile,
clearTile, clearTile,
Padding( Padding(
padding: const EdgeInsets.only(bottom: 1), padding: const EdgeInsets.only(bottom: 1),
child: ExpansionPanelList( child: ExpansionPanelList(
expansionCallback: (index, isExpanded) { expansionCallback: (index, isExpanded) {
setState(() => _showOptions = !isExpanded); setState(() => _showOptions = !isExpanded);
}, },
animationDuration: animationDuration, animationDuration: animationDuration,
expandedHeaderPadding: EdgeInsets.zero, expandedHeaderPadding: EdgeInsets.zero,
elevation: 0, elevation: 0,
children: [ children: [
ExpansionPanel( ExpansionPanel(
headerBuilder: (context, isExpanded) => ListTile( headerBuilder: (context, isExpanded) => ListTile(
title: Text(l10n.editEntryDateDialogFieldSelection), 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( actions: [
value: _fields.contains(field), TextButton(
onChanged: (selected) => setState(() => selected ? _fields.add(field) : _fields.remove(field)), onPressed: () => Navigator.pop(context),
title: Text(_fieldTitle(field)), child: Text(MaterialLocalizations.of(context).cancelButtonLabel),
)) ),
.toList(), TextButton(
), onPressed: () => _submit(context),
isExpanded: _showOptions, child: Text(l10n.applyButtonLabel),
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),
),
],
), ),
); );
} }

View file

@ -168,8 +168,10 @@ class _DateRow extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final locale = context.l10n.localeName; final locale = context.l10n.localeName;
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
final date = entry?.bestDate; 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( return Row(
children: [ children: [
const SizedBox(width: MapInfoRow.iconPadding), const SizedBox(width: MapInfoRow.iconPadding),

View file

@ -17,6 +17,7 @@ import 'package:aves/widgets/viewer/info/common.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class BasicSection extends StatelessWidget { class BasicSection extends StatelessWidget {
final AvesEntry entry; final AvesEntry entry;
@ -41,6 +42,7 @@ class BasicSection extends StatelessWidget {
final l10n = context.l10n; final l10n = context.l10n;
final infoUnknown = l10n.viewerInfoUnknown; final infoUnknown = l10n.viewerInfoUnknown;
final locale = l10n.localeName; final locale = l10n.localeName;
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
return AnimatedBuilder( return AnimatedBuilder(
animation: entry.metadataChangeNotifier, 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) // inserting ZWSP (\u200B) between characters does help, but it messes with width and height computation (another Flutter issue)
final title = entry.bestTitle ?? infoUnknown; final title = entry.bestTitle ?? infoUnknown;
final date = entry.bestDate; 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 showResolution = !entry.isSvg && entry.isSized;
final sizeText = entry.sizeBytes != null ? formatFilesize(entry.sizeBytes!) : infoUnknown; final sizeText = entry.sizeBytes != null ? formatFilesize(entry.sizeBytes!) : infoUnknown;
final path = entry.path; final path = entry.path;

View file

@ -395,8 +395,10 @@ class _DateRow extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final locale = context.l10n.localeName; final locale = context.l10n.localeName;
final use24hour = context.select<MediaQueryData, bool>((v) => v.alwaysUse24HourFormat);
final date = entry.bestDate; 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 final resolutionText = entry.isSvg
? entry.aspectRatioText ? entry.aspectRatioText
: entry.isSized : entry.isSized