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.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")

View file

@ -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,
};
}));

View file

@ -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');

View file

@ -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),
),
],
);
},
),
);
}

View file

@ -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),

View file

@ -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;

View file

@ -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