@@ -8,6 +8,7 @@ import '../../models/livetv_channel.dart';
|
||||
import '../../models/livetv_program.dart';
|
||||
import '../../providers/multi_server_provider.dart';
|
||||
import '../../theme/mono_tokens.dart';
|
||||
import '../../utils/formatters.dart';
|
||||
import '../../utils/live_tv_player_navigation.dart';
|
||||
import '../../utils/plex_image_helper.dart';
|
||||
import '../../widgets/app_icon.dart';
|
||||
@@ -173,7 +174,7 @@ class _ScheduleListTile extends StatelessWidget {
|
||||
|
||||
const _ScheduleListTile({required this.program, required this.channel, required this.onTap});
|
||||
|
||||
String _formatTimeInfo() {
|
||||
String _formatTimeInfo({required bool is24Hour}) {
|
||||
final now = DateTime.now();
|
||||
final start = program.startTime;
|
||||
final end = program.endTime;
|
||||
@@ -187,16 +188,16 @@ class _ScheduleListTile extends StatelessWidget {
|
||||
final minutesUntil = start.difference(now).inMinutes;
|
||||
if (minutesUntil <= 0) {
|
||||
// Just started
|
||||
return _formatAbsoluteTime(start, now);
|
||||
return _formatAbsoluteTime(start, now, is24Hour: is24Hour);
|
||||
} else if (minutesUntil < 90) {
|
||||
return 'Starting in ${minutesUntil}min';
|
||||
} else {
|
||||
return _formatAbsoluteTime(start, now);
|
||||
return _formatAbsoluteTime(start, now, is24Hour: is24Hour);
|
||||
}
|
||||
}
|
||||
|
||||
String _formatAbsoluteTime(DateTime start, DateTime now) {
|
||||
final time = '${start.hour.toString().padLeft(2, '0')}:${start.minute.toString().padLeft(2, '0')}';
|
||||
String _formatAbsoluteTime(DateTime start, DateTime now, {required bool is24Hour}) {
|
||||
final time = formatClockTime(start, is24Hour: is24Hour);
|
||||
final today = DateTime(now.year, now.month, now.day);
|
||||
final startDay = DateTime(start.year, start.month, start.day);
|
||||
final diff = startDay.difference(today).inDays;
|
||||
@@ -217,7 +218,7 @@ class _ScheduleListTile extends StatelessWidget {
|
||||
? 'S${program.parentIndex} · E${program.index} — ${program.title}'
|
||||
: program.title;
|
||||
|
||||
final timeInfo = _formatTimeInfo();
|
||||
final timeInfo = _formatTimeInfo(is24Hour: MediaQuery.alwaysUse24HourFormatOf(context));
|
||||
final subtitle = [
|
||||
timeInfo,
|
||||
if (program.summary != null && program.summary!.isNotEmpty) program.summary!,
|
||||
|
||||
@@ -194,7 +194,7 @@ class _ProgramDetailsSheetContentState extends State<_ProgramDetailsSheetContent
|
||||
[
|
||||
if (channel != null) channel.displayName,
|
||||
if (program.startTime != null && program.endTime != null)
|
||||
'${program.startTime!.hour.toString().padLeft(2, '0')}:${program.startTime!.minute.toString().padLeft(2, '0')} - ${program.endTime!.hour.toString().padLeft(2, '0')}:${program.endTime!.minute.toString().padLeft(2, '0')}',
|
||||
'${formatClockTime(program.startTime!, is24Hour: MediaQuery.alwaysUse24HourFormatOf(context))} - ${formatClockTime(program.endTime!, is24Hour: MediaQuery.alwaysUse24HourFormatOf(context))}',
|
||||
if (program.durationMinutes > 0) formatDurationTextual(program.durationMinutes * 60000),
|
||||
].join(' · '),
|
||||
style: theme.textTheme.bodySmall?.copyWith(color: theme.colorScheme.onSurfaceVariant),
|
||||
|
||||
@@ -845,11 +845,12 @@ class GuideTabState extends State<GuideTab> {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Widget _buildTimeHeader(ThemeData theme) {
|
||||
final is24Hour = MediaQuery.alwaysUse24HourFormatOf(context);
|
||||
final slots = <Widget>[];
|
||||
var current = _gridStart;
|
||||
|
||||
while (current.isBefore(_gridEnd)) {
|
||||
final timeStr = '${current.hour.toString().padLeft(2, '0')}:${current.minute.toString().padLeft(2, '0')}';
|
||||
final timeStr = formatClockTime(current, is24Hour: is24Hour);
|
||||
slots.add(
|
||||
SizedBox(
|
||||
width: _slotWidth,
|
||||
@@ -1064,7 +1065,7 @@ class GuideTabState extends State<GuideTab> {
|
||||
),
|
||||
if (program.startTime != null)
|
||||
Text(
|
||||
'${program.startTime!.hour.toString().padLeft(2, '0')}:${program.startTime!.minute.toString().padLeft(2, '0')} · ${formatDurationTextual(program.durationMinutes * 60000)}',
|
||||
'${formatClockTime(program.startTime!, is24Hour: MediaQuery.alwaysUse24HourFormatOf(context))} · ${formatDurationTextual(program.durationMinutes * 60000)}',
|
||||
style: theme.textTheme.labelSmall?.copyWith(color: subtitleColor),
|
||||
maxLines: 1,
|
||||
),
|
||||
|
||||
@@ -176,13 +176,20 @@ DurationLocale _getDurationLocale() {
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats a [DateTime] as a clock time string, respecting the system 24-hour preference.
|
||||
/// Uses `DateFormat.Hm` for 24-hour format and `DateFormat.jm` for 12-hour format.
|
||||
String formatClockTime(DateTime time, {required bool is24Hour}) {
|
||||
final locale = LocaleSettings.currentLocale.languageCode;
|
||||
final formatter = is24Hour ? DateFormat.Hm(locale) : DateFormat.jm(locale);
|
||||
return formatter.format(time);
|
||||
}
|
||||
|
||||
/// Formats the clock time at which media will finish playing, given the remaining duration.
|
||||
/// Returns a localized time string like "6:12 PM" or "18:12" depending on locale.
|
||||
String formatFinishTime(Duration remaining, {double rate = 1.0}) {
|
||||
/// Returns a localized time string like "6:12 PM" or "18:12" depending on system setting.
|
||||
String formatFinishTime(Duration remaining, {double rate = 1.0, required bool is24Hour}) {
|
||||
final adjustedRemaining = remaining * (1.0 / rate);
|
||||
final finishTime = DateTime.now().add(adjustedRemaining);
|
||||
final formatter = DateFormat.jm(LocaleSettings.currentLocale.languageCode);
|
||||
return formatter.format(finishTime);
|
||||
return formatClockTime(finishTime, is24Hour: is24Hour);
|
||||
}
|
||||
|
||||
/// Takes a list of strings and returns one long string with each item in the list concatenated by a bullet
|
||||
|
||||
@@ -633,7 +633,7 @@ class DesktopVideoControlsState extends State<DesktopVideoControls> {
|
||||
final rate = rateSnap.data ?? 1.0;
|
||||
if (remaining.inSeconds <= 0) return const SizedBox.shrink();
|
||||
|
||||
final text = t.videoControls.endsAt(time: formatFinishTime(remaining, rate: rate));
|
||||
final text = t.videoControls.endsAt(time: formatFinishTime(remaining, rate: rate, is24Hour: MediaQuery.alwaysUse24HourFormatOf(context)));
|
||||
const style = TextStyle(color: Colors.white70, fontSize: 13);
|
||||
|
||||
return LayoutBuilder(
|
||||
|
||||
@@ -127,7 +127,7 @@ class VideoTimelineBar extends StatelessWidget {
|
||||
initialData: player.state.rate,
|
||||
builder: (context, rateSnap) {
|
||||
final rate = rateSnap.data ?? 1.0;
|
||||
final text = '${formatDurationTimestamp(remaining)} · ${formatFinishTime(remaining.abs(), rate: rate)}';
|
||||
final text = '${formatDurationTimestamp(remaining)} · ${formatFinishTime(remaining.abs(), rate: rate, is24Hour: MediaQuery.alwaysUse24HourFormatOf(context))}';
|
||||
return Text(text, style: const TextStyle(color: Colors.white, fontSize: 14));
|
||||
},
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user