#437 tv: read-only, ambient mode, webview, settings
This commit is contained in:
parent
829ec201eb
commit
e5e1a8f275
27 changed files with 221 additions and 124 deletions
|
@ -11,6 +11,13 @@ This change eventually prevents building the app with Flutter v3.3.3.
|
||||||
package="deckers.thibault.aves"
|
package="deckers.thibault.aves"
|
||||||
android:installLocation="auto">
|
android:installLocation="auto">
|
||||||
|
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.hardware.touchscreen"
|
||||||
|
android:required="false" />
|
||||||
|
<uses-feature
|
||||||
|
android:name="android.software.leanback"
|
||||||
|
android:required="false" />
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Scoped storage on Android 10 is inconvenient because users need to confirm edition on each individual file.
|
Scoped storage on Android 10 is inconvenient because users need to confirm edition on each individual file.
|
||||||
So we request `WRITE_EXTERNAL_STORAGE` until Android 10 (API 29), and enable `requestLegacyExternalStorage`
|
So we request `WRITE_EXTERNAL_STORAGE` until Android 10 (API 29), and enable `requestLegacyExternalStorage`
|
||||||
|
@ -67,6 +74,7 @@ This change eventually prevents building the app with Flutter v3.3.3.
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:appCategory="image"
|
android:appCategory="image"
|
||||||
|
android:banner="@drawable/banner"
|
||||||
android:fullBackupOnly="true"
|
android:fullBackupOnly="true"
|
||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
|
@ -83,6 +91,8 @@ This change eventually prevents building the app with Flutter v3.3.3.
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|
|
@ -20,11 +20,15 @@ class ActivityWindowHandler(private val activity: Activity) : WindowHandler(acti
|
||||||
|
|
||||||
val window = activity.window
|
val window = activity.window
|
||||||
val flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
val flag = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
|
||||||
|
|
||||||
|
val old = (window.attributes.flags and flag) != 0
|
||||||
|
if (old != on) {
|
||||||
if (on) {
|
if (on) {
|
||||||
window.addFlags(flag)
|
window.addFlags(flag)
|
||||||
} else {
|
} else {
|
||||||
window.clearFlags(flag)
|
window.clearFlags(flag)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
result.success(null)
|
result.success(null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
BIN
android/app/src/main/res/drawable-nodpi/banner.png
Normal file
BIN
android/app/src/main/res/drawable-nodpi/banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 7 KiB |
|
@ -195,6 +195,7 @@
|
||||||
"nameConflictStrategySkip": "Skip",
|
"nameConflictStrategySkip": "Skip",
|
||||||
|
|
||||||
"keepScreenOnNever": "Never",
|
"keepScreenOnNever": "Never",
|
||||||
|
"keepScreenOnVideoPlayback": "During video playback",
|
||||||
"keepScreenOnViewerOnly": "Viewer page only",
|
"keepScreenOnViewerOnly": "Viewer page only",
|
||||||
"keepScreenOnAlways": "Always",
|
"keepScreenOnAlways": "Always",
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
|
|
||||||
final Device device = Device._private();
|
final Device device = Device._private();
|
||||||
|
@ -6,7 +7,7 @@ final Device device = Device._private();
|
||||||
class Device {
|
class Device {
|
||||||
late final String _userAgent;
|
late final String _userAgent;
|
||||||
late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderFlagEmojis, _canRequestManageMedia, _canSetLockScreenWallpaper;
|
late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderFlagEmojis, _canRequestManageMedia, _canSetLockScreenWallpaper;
|
||||||
late final bool _hasGeocoder, _isDynamicColorAvailable, _showPinShortcutFeedback, _supportEdgeToEdgeUIMode;
|
late final bool _hasGeocoder, _isDynamicColorAvailable, _isTelevision, _showPinShortcutFeedback, _supportEdgeToEdgeUIMode;
|
||||||
|
|
||||||
String get userAgent => _userAgent;
|
String get userAgent => _userAgent;
|
||||||
|
|
||||||
|
@ -26,6 +27,10 @@ class Device {
|
||||||
|
|
||||||
bool get isDynamicColorAvailable => _isDynamicColorAvailable;
|
bool get isDynamicColorAvailable => _isDynamicColorAvailable;
|
||||||
|
|
||||||
|
bool get isReadOnly => _isTelevision;
|
||||||
|
|
||||||
|
bool get isTelevision => _isTelevision;
|
||||||
|
|
||||||
bool get showPinShortcutFeedback => _showPinShortcutFeedback;
|
bool get showPinShortcutFeedback => _showPinShortcutFeedback;
|
||||||
|
|
||||||
bool get supportEdgeToEdgeUIMode => _supportEdgeToEdgeUIMode;
|
bool get supportEdgeToEdgeUIMode => _supportEdgeToEdgeUIMode;
|
||||||
|
@ -36,6 +41,9 @@ class Device {
|
||||||
final packageInfo = await PackageInfo.fromPlatform();
|
final packageInfo = await PackageInfo.fromPlatform();
|
||||||
_userAgent = '${packageInfo.packageName}/${packageInfo.version}';
|
_userAgent = '${packageInfo.packageName}/${packageInfo.version}';
|
||||||
|
|
||||||
|
final androidInfo = await DeviceInfoPlugin().androidInfo;
|
||||||
|
_isTelevision = androidInfo.systemFeatures.contains('android.software.leanback');
|
||||||
|
|
||||||
final capabilities = await deviceService.getCapabilities();
|
final capabilities = await deviceService.getCapabilities();
|
||||||
_canGrantDirectoryAccess = capabilities['canGrantDirectoryAccess'] ?? false;
|
_canGrantDirectoryAccess = capabilities['canGrantDirectoryAccess'] ?? false;
|
||||||
_canPinShortcut = capabilities['canPinShortcut'] ?? false;
|
_canPinShortcut = capabilities['canPinShortcut'] ?? false;
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
import 'package:aves/geo/countries.dart';
|
import 'package:aves/geo/countries.dart';
|
||||||
|
import 'package:aves/model/device.dart';
|
||||||
import 'package:aves/model/entry_cache.dart';
|
import 'package:aves/model/entry_cache.dart';
|
||||||
import 'package:aves/model/entry_dirs.dart';
|
import 'package:aves/model/entry_dirs.dart';
|
||||||
import 'package:aves/model/favourites.dart';
|
import 'package:aves/model/favourites.dart';
|
||||||
|
@ -280,7 +281,7 @@ class AvesEntry {
|
||||||
|
|
||||||
bool get isMediaStoreMediaContent => isMediaStoreContent && {'/external/images/', '/external/video/'}.any(uri.contains);
|
bool get isMediaStoreMediaContent => isMediaStoreContent && {'/external/images/', '/external/video/'}.any(uri.contains);
|
||||||
|
|
||||||
bool get canEdit => path != null && !trashed && isMediaStoreContent;
|
bool get canEdit => !device.isReadOnly && path != null && !trashed && isMediaStoreContent;
|
||||||
|
|
||||||
bool get canEditDate => canEdit && (canEditExif || canEditXmp);
|
bool get canEditDate => canEdit && (canEditExif || canEditXmp);
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ enum EntryBackground { black, white, checkered }
|
||||||
|
|
||||||
enum HomePageSetting { collection, albums }
|
enum HomePageSetting { collection, albums }
|
||||||
|
|
||||||
enum KeepScreenOn { never, viewerOnly, always }
|
enum KeepScreenOn { never, videoPlayback, viewerOnly, always }
|
||||||
|
|
||||||
enum SlideshowVideoPlayback { skip, playMuted, playWithSound }
|
enum SlideshowVideoPlayback { skip, playMuted, playWithSound }
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,8 @@ extension ExtraKeepScreenOn on KeepScreenOn {
|
||||||
switch (this) {
|
switch (this) {
|
||||||
case KeepScreenOn.never:
|
case KeepScreenOn.never:
|
||||||
return context.l10n.keepScreenOnNever;
|
return context.l10n.keepScreenOnNever;
|
||||||
|
case KeepScreenOn.videoPlayback:
|
||||||
|
return context.l10n.keepScreenOnVideoPlayback;
|
||||||
case KeepScreenOn.viewerOnly:
|
case KeepScreenOn.viewerOnly:
|
||||||
return context.l10n.keepScreenOnViewerOnly;
|
return context.l10n.keepScreenOnViewerOnly;
|
||||||
case KeepScreenOn.always:
|
case KeepScreenOn.always:
|
||||||
|
|
|
@ -5,7 +5,11 @@ import 'dart:math';
|
||||||
import 'package:aves/app_flavor.dart';
|
import 'package:aves/app_flavor.dart';
|
||||||
import 'package:aves/model/actions/entry_actions.dart';
|
import 'package:aves/model/actions/entry_actions.dart';
|
||||||
import 'package:aves/model/actions/entry_set_actions.dart';
|
import 'package:aves/model/actions/entry_set_actions.dart';
|
||||||
|
import 'package:aves/model/device.dart';
|
||||||
|
import 'package:aves/model/filters/favourite.dart';
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
|
import 'package:aves/model/filters/mime.dart';
|
||||||
|
import 'package:aves/model/filters/recent.dart';
|
||||||
import 'package:aves/model/settings/defaults.dart';
|
import 'package:aves/model/settings/defaults.dart';
|
||||||
import 'package:aves/model/settings/enums/enums.dart';
|
import 'package:aves/model/settings/enums/enums.dart';
|
||||||
import 'package:aves/model/settings/enums/map_style.dart';
|
import 'package:aves/model/settings/enums/map_style.dart';
|
||||||
|
@ -230,6 +234,25 @@ class Settings extends ChangeNotifier {
|
||||||
mapStyle = styles[Random().nextInt(styles.length)];
|
mapStyle = styles[Random().nextInt(styles.length)];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (device.isTelevision) {
|
||||||
|
drawerTypeBookmarks = [
|
||||||
|
null,
|
||||||
|
MimeFilter.video,
|
||||||
|
FavouriteFilter.instance,
|
||||||
|
RecentlyAddedFilter.instance,
|
||||||
|
];
|
||||||
|
mustBackTwiceToExit = false;
|
||||||
|
keepScreenOn = KeepScreenOn.videoPlayback;
|
||||||
|
enableBottomNavigationBar = false;
|
||||||
|
viewerGestureSideTapNext = false;
|
||||||
|
viewerUseCutout = true;
|
||||||
|
viewerMaxBrightness = false;
|
||||||
|
videoControls = VideoControls.playSeek;
|
||||||
|
videoGestureDoubleTapTogglePlay = false;
|
||||||
|
videoGestureSideDoubleTapSeek = false;
|
||||||
|
enableBin = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// app
|
// app
|
||||||
|
|
|
@ -11,6 +11,7 @@ import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/theme/colors.dart';
|
import 'package:aves/theme/colors.dart';
|
||||||
import 'package:aves/theme/durations.dart';
|
import 'package:aves/theme/durations.dart';
|
||||||
import 'package:aves/utils/constants.dart';
|
import 'package:aves/utils/constants.dart';
|
||||||
|
import 'package:aves/widgets/aves_app.dart';
|
||||||
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
|
||||||
|
@ -21,7 +22,6 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import 'package:package_info_plus/package_info_plus.dart';
|
import 'package:package_info_plus/package_info_plus.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
class BugReport extends StatefulWidget {
|
class BugReport extends StatefulWidget {
|
||||||
const BugReport({super.key});
|
const BugReport({super.key});
|
||||||
|
@ -34,7 +34,7 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
|
||||||
late Future<String> _infoLoader;
|
late Future<String> _infoLoader;
|
||||||
bool _showInstructions = false;
|
bool _showInstructions = false;
|
||||||
|
|
||||||
static final bugReportUri = Uri.parse('${Constants.avesGithub}/issues/new?labels=type%3Abug&template=bug_report.md');
|
static const bugReportUrl = '${Constants.avesGithub}/issues/new?labels=type%3Abug&template=bug_report.md';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -184,9 +184,5 @@ class _BugReportState extends State<BugReport> with FeedbackMixin {
|
||||||
showFeedback(context, context.l10n.genericSuccessFeedback);
|
showFeedback(context, context.l10n.genericSuccessFeedback);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _goToGithub() async {
|
Future<void> _goToGithub() => AvesApp.launchUrl(bugReportUrl);
|
||||||
if (await canLaunchUrl(bugReportUri)) {
|
|
||||||
await launchUrl(bugReportUri, mode: LaunchMode.externalApplication);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ import 'package:material_color_utilities/material_color_utilities.dart';
|
||||||
import 'package:overlay_support/overlay_support.dart';
|
import 'package:overlay_support/overlay_support.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:tuple/tuple.dart';
|
import 'package:tuple/tuple.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart' as ul;
|
||||||
|
|
||||||
class AvesApp extends StatefulWidget {
|
class AvesApp extends StatefulWidget {
|
||||||
final AppFlavor flavor;
|
final AppFlavor flavor;
|
||||||
|
@ -103,6 +104,19 @@ class AvesApp extends StatefulWidget {
|
||||||
static Future<void> hideSystemUI() async {
|
static Future<void> hideSystemUI() async {
|
||||||
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
await SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersive);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Future<void> launchUrl(String? urlString) async {
|
||||||
|
if (urlString != null) {
|
||||||
|
final url = Uri.parse(urlString);
|
||||||
|
if (await ul.canLaunchUrl(url)) {
|
||||||
|
try {
|
||||||
|
await ul.launchUrl(url, mode: device.isTelevision ? ul.LaunchMode.inAppWebView : ul.LaunchMode.externalApplication);
|
||||||
|
} catch (error, stack) {
|
||||||
|
debugPrint('failed to open url=$urlString with error=$error\n$stack');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||||
|
@ -207,13 +221,20 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||||
lightAccent = Color(tonalPalette?.get(60) ?? defaultAccent.value);
|
lightAccent = Color(tonalPalette?.get(60) ?? defaultAccent.value);
|
||||||
darkAccent = Color(tonalPalette?.get(70) ?? defaultAccent.value);
|
darkAccent = Color(tonalPalette?.get(70) ?? defaultAccent.value);
|
||||||
}
|
}
|
||||||
|
final lightTheme = Themes.lightTheme(lightAccent, initialized);
|
||||||
|
final darkTheme = themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme(darkAccent, initialized) : Themes.darkTheme(darkAccent, initialized);
|
||||||
return FutureBuilder<bool>(
|
return FutureBuilder<bool>(
|
||||||
future: _shouldUseBoldFontLoader,
|
future: _shouldUseBoldFontLoader,
|
||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
// Flutter v3.4 already checks the system `Configuration.fontWeightAdjustment` to update `MediaQuery`
|
// Flutter v3.4 already checks the system `Configuration.fontWeightAdjustment` to update `MediaQuery`
|
||||||
// but we need to also check the non-standard Samsung field `bf` representing the bold font toggle
|
// but we need to also check the non-standard Samsung field `bf` representing the bold font toggle
|
||||||
final shouldUseBoldFont = snapshot.data ?? false;
|
final shouldUseBoldFont = snapshot.data ?? false;
|
||||||
return MaterialApp(
|
return Shortcuts(
|
||||||
|
shortcuts: <LogicalKeySet, Intent>{
|
||||||
|
// handle Android TV remote `select` button
|
||||||
|
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
|
||||||
|
},
|
||||||
|
child: MaterialApp(
|
||||||
navigatorKey: AvesApp.navigatorKey,
|
navigatorKey: AvesApp.navigatorKey,
|
||||||
home: home,
|
home: home,
|
||||||
navigatorObservers: _navigatorObservers,
|
navigatorObservers: _navigatorObservers,
|
||||||
|
@ -234,14 +255,15 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
onGenerateTitle: (context) => context.l10n.appName,
|
onGenerateTitle: (context) => context.l10n.appName,
|
||||||
theme: Themes.lightTheme(lightAccent, initialized),
|
theme: lightTheme,
|
||||||
darkTheme: themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme(darkAccent, initialized) : Themes.darkTheme(darkAccent, initialized),
|
darkTheme: darkTheme,
|
||||||
themeMode: themeBrightness.appThemeMode,
|
themeMode: themeBrightness.appThemeMode,
|
||||||
locale: settingsLocale,
|
locale: settingsLocale,
|
||||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||||
supportedLocales: AvesApp.supportedLocales,
|
supportedLocales: AvesApp.supportedLocales,
|
||||||
// TODO TLAD remove custom scroll behavior when this is fixed: https://github.com/flutter/flutter/issues/82906
|
// TODO TLAD remove custom scroll behavior when this is fixed: https://github.com/flutter/flutter/issues/82906
|
||||||
scrollBehavior: StretchMaterialScrollBehavior(),
|
scrollBehavior: StretchMaterialScrollBehavior(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:ui';
|
||||||
|
|
||||||
import 'package:aves/app_mode.dart';
|
import 'package:aves/app_mode.dart';
|
||||||
import 'package:aves/model/actions/entry_set_actions.dart';
|
import 'package:aves/model/actions/entry_set_actions.dart';
|
||||||
|
import 'package:aves/model/device.dart';
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/model/filters/query.dart';
|
import 'package:aves/model/filters/query.dart';
|
||||||
|
@ -307,7 +308,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
||||||
...(isSelecting ? selectionMenuActions : browsingMenuActions).where(isVisible).map(
|
...(isSelecting ? selectionMenuActions : browsingMenuActions).where(isVisible).map(
|
||||||
(action) => _toMenuItem(action, enabled: canApply(action), selection: selection),
|
(action) => _toMenuItem(action, enabled: canApply(action), selection: selection),
|
||||||
),
|
),
|
||||||
if (isSelecting && !isTrash && appMode == AppMode.main)
|
if (isSelecting && !device.isReadOnly && appMode == AppMode.main && !isTrash)
|
||||||
PopupMenuItem<EntrySetAction>(
|
PopupMenuItem<EntrySetAction>(
|
||||||
enabled: canApplyEditActions,
|
enabled: canApplyEditActions,
|
||||||
padding: EdgeInsets.zero,
|
padding: EdgeInsets.zero,
|
||||||
|
|
|
@ -73,7 +73,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
||||||
case EntrySetAction.addShortcut:
|
case EntrySetAction.addShortcut:
|
||||||
return appMode == AppMode.main && !isSelecting && device.canPinShortcut && !isTrash;
|
return appMode == AppMode.main && !isSelecting && device.canPinShortcut && !isTrash;
|
||||||
case EntrySetAction.emptyBin:
|
case EntrySetAction.emptyBin:
|
||||||
return appMode == AppMode.main && isTrash;
|
return !device.isReadOnly && appMode == AppMode.main && isTrash;
|
||||||
// browsing or selecting
|
// browsing or selecting
|
||||||
case EntrySetAction.map:
|
case EntrySetAction.map:
|
||||||
case EntrySetAction.slideshow:
|
case EntrySetAction.slideshow:
|
||||||
|
@ -82,13 +82,14 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
||||||
case EntrySetAction.rescan:
|
case EntrySetAction.rescan:
|
||||||
return appMode == AppMode.main && !isTrash;
|
return appMode == AppMode.main && !isTrash;
|
||||||
// selecting
|
// selecting
|
||||||
case EntrySetAction.delete:
|
|
||||||
return appMode == AppMode.main && isSelecting;
|
|
||||||
case EntrySetAction.share:
|
case EntrySetAction.share:
|
||||||
|
case EntrySetAction.toggleFavourite:
|
||||||
|
return appMode == AppMode.main && isSelecting && !isTrash;
|
||||||
|
case EntrySetAction.delete:
|
||||||
|
return !device.isReadOnly && appMode == AppMode.main && isSelecting;
|
||||||
case EntrySetAction.copy:
|
case EntrySetAction.copy:
|
||||||
case EntrySetAction.move:
|
case EntrySetAction.move:
|
||||||
case EntrySetAction.rename:
|
case EntrySetAction.rename:
|
||||||
case EntrySetAction.toggleFavourite:
|
|
||||||
case EntrySetAction.rotateCCW:
|
case EntrySetAction.rotateCCW:
|
||||||
case EntrySetAction.rotateCW:
|
case EntrySetAction.rotateCW:
|
||||||
case EntrySetAction.flip:
|
case EntrySetAction.flip:
|
||||||
|
@ -98,9 +99,9 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
||||||
case EntrySetAction.editRating:
|
case EntrySetAction.editRating:
|
||||||
case EntrySetAction.editTags:
|
case EntrySetAction.editTags:
|
||||||
case EntrySetAction.removeMetadata:
|
case EntrySetAction.removeMetadata:
|
||||||
return appMode == AppMode.main && isSelecting && !isTrash;
|
return !device.isReadOnly && appMode == AppMode.main && isSelecting && !isTrash;
|
||||||
case EntrySetAction.restore:
|
case EntrySetAction.restore:
|
||||||
return appMode == AppMode.main && isSelecting && isTrash;
|
return !device.isReadOnly && appMode == AppMode.main && isSelecting && isTrash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import 'package:aves/theme/icons.dart';
|
import 'package:aves/theme/icons.dart';
|
||||||
|
import 'package:aves/widgets/aves_app.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
class LinkChip extends StatelessWidget {
|
class LinkChip extends StatelessWidget {
|
||||||
final Widget? leading;
|
final Widget? leading;
|
||||||
|
@ -24,20 +24,11 @@ class LinkChip extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final _urlString = urlString;
|
|
||||||
return DefaultTextStyle.merge(
|
return DefaultTextStyle.merge(
|
||||||
style: (textStyle ?? const TextStyle()).copyWith(color: color),
|
style: (textStyle ?? const TextStyle()).copyWith(color: color),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: borderRadius,
|
borderRadius: borderRadius,
|
||||||
onTap: onTap ??
|
onTap: onTap ?? () => AvesApp.launchUrl(urlString),
|
||||||
() async {
|
|
||||||
if (_urlString != null) {
|
|
||||||
final url = Uri.parse(_urlString);
|
|
||||||
if (await canLaunchUrl(url)) {
|
|
||||||
await launchUrl(url, mode: LaunchMode.externalApplication);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
|
import 'package:aves/widgets/aves_app.dart';
|
||||||
import 'package:aves/widgets/common/fx/borders.dart';
|
import 'package:aves/widgets/common/fx/borders.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
class MarkdownContainer extends StatelessWidget {
|
class MarkdownContainer extends StatelessWidget {
|
||||||
final String data;
|
final String data;
|
||||||
|
@ -43,14 +43,7 @@ class MarkdownContainer extends StatelessWidget {
|
||||||
child: Markdown(
|
child: Markdown(
|
||||||
data: data,
|
data: data,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
onTapLink: (text, href, title) async {
|
onTapLink: (text, href, title) => AvesApp.launchUrl(href),
|
||||||
if (href != null) {
|
|
||||||
final url = Uri.parse(href);
|
|
||||||
if (await canLaunchUrl(url)) {
|
|
||||||
await launchUrl(url, mode: LaunchMode.externalApplication);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
|
import 'package:aves/widgets/aves_app.dart';
|
||||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||||
import 'package:aves/widgets/viewer/info/common.dart';
|
import 'package:aves/widgets/viewer/info/common.dart';
|
||||||
import 'package:aves_map/aves_map.dart';
|
import 'package:aves_map/aves_map.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
class Attribution extends StatelessWidget {
|
class Attribution extends StatelessWidget {
|
||||||
final EntryMapStyle? style;
|
final EntryMapStyle? style;
|
||||||
|
@ -37,14 +37,7 @@ class Attribution extends StatelessWidget {
|
||||||
a: TextStyle(color: theme.colorScheme.secondary),
|
a: TextStyle(color: theme.colorScheme.secondary),
|
||||||
p: theme.textTheme.bodySmall!.merge(const TextStyle(fontSize: InfoRowGroup.fontSize)),
|
p: theme.textTheme.bodySmall!.merge(const TextStyle(fontSize: InfoRowGroup.fontSize)),
|
||||||
),
|
),
|
||||||
onTapLink: (text, href, title) async {
|
onTapLink: (text, href, title) => AvesApp.launchUrl(href),
|
||||||
if (href != null) {
|
|
||||||
final url = Uri.parse(href);
|
|
||||||
if (await canLaunchUrl(url)) {
|
|
||||||
await launchUrl(url, mode: LaunchMode.externalApplication);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:io';
|
||||||
import 'package:aves/app_mode.dart';
|
import 'package:aves/app_mode.dart';
|
||||||
import 'package:aves/model/actions/chip_set_actions.dart';
|
import 'package:aves/model/actions/chip_set_actions.dart';
|
||||||
import 'package:aves/model/actions/move_type.dart';
|
import 'package:aves/model/actions/move_type.dart';
|
||||||
|
import 'package:aves/model/device.dart';
|
||||||
import 'package:aves/model/filters/album.dart';
|
import 'package:aves/model/filters/album.dart';
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
import 'package:aves/model/highlight.dart';
|
import 'package:aves/model/highlight.dart';
|
||||||
|
@ -77,7 +78,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with
|
||||||
return appMode == AppMode.main && !isSelecting;
|
return appMode == AppMode.main && !isSelecting;
|
||||||
case ChipSetAction.delete:
|
case ChipSetAction.delete:
|
||||||
case ChipSetAction.rename:
|
case ChipSetAction.rename:
|
||||||
return appMode == AppMode.main && isSelecting;
|
return !device.isReadOnly && appMode == AppMode.main && isSelecting;
|
||||||
default:
|
default:
|
||||||
return super.isVisible(
|
return super.isVisible(
|
||||||
action,
|
action,
|
||||||
|
|
|
@ -33,7 +33,7 @@ class DisplaySection extends SettingsSection {
|
||||||
SettingsTileDisplayThemeColorMode(),
|
SettingsTileDisplayThemeColorMode(),
|
||||||
if (device.isDynamicColorAvailable) SettingsTileDisplayEnableDynamicColor(),
|
if (device.isDynamicColorAvailable) SettingsTileDisplayEnableDynamicColor(),
|
||||||
SettingsTileDisplayEnableBlurEffect(),
|
SettingsTileDisplayEnableBlurEffect(),
|
||||||
SettingsTileDisplayDisplayRefreshRateMode(),
|
if (!device.isTelevision) SettingsTileDisplayDisplayRefreshRateMode(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:aves/model/device.dart';
|
||||||
import 'package:aves/model/settings/enums/enums.dart';
|
import 'package:aves/model/settings/enums/enums.dart';
|
||||||
import 'package:aves/model/settings/enums/home_page.dart';
|
import 'package:aves/model/settings/enums/home_page.dart';
|
||||||
import 'package:aves/model/settings/enums/screen_on.dart';
|
import 'package:aves/model/settings/enums/screen_on.dart';
|
||||||
|
@ -31,11 +32,11 @@ class NavigationSection extends SettingsSection {
|
||||||
@override
|
@override
|
||||||
FutureOr<List<SettingsTile>> tiles(BuildContext context) => [
|
FutureOr<List<SettingsTile>> tiles(BuildContext context) => [
|
||||||
SettingsTileNavigationHomePage(),
|
SettingsTileNavigationHomePage(),
|
||||||
SettingsTileShowBottomNavigationBar(),
|
if (!device.isTelevision) SettingsTileShowBottomNavigationBar(),
|
||||||
SettingsTileNavigationDrawer(),
|
SettingsTileNavigationDrawer(),
|
||||||
SettingsTileNavigationConfirmationDialog(),
|
if (!device.isTelevision) SettingsTileNavigationConfirmationDialog(),
|
||||||
SettingsTileNavigationKeepScreenOn(),
|
if (!device.isTelevision) SettingsTileNavigationKeepScreenOn(),
|
||||||
SettingsTileNavigationDoubleBackExit(),
|
if (!device.isTelevision) SettingsTileNavigationDoubleBackExit(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,11 +34,11 @@ class PrivacySection extends SettingsSection {
|
||||||
return [
|
return [
|
||||||
SettingsTilePrivacyAllowInstalledAppAccess(),
|
SettingsTilePrivacyAllowInstalledAppAccess(),
|
||||||
if (canEnableErrorReporting) SettingsTilePrivacyAllowErrorReporting(),
|
if (canEnableErrorReporting) SettingsTilePrivacyAllowErrorReporting(),
|
||||||
if (device.canRequestManageMedia) SettingsTilePrivacyManageMedia(),
|
if (!device.isTelevision && device.canRequestManageMedia) SettingsTilePrivacyManageMedia(),
|
||||||
SettingsTilePrivacySaveSearchHistory(),
|
SettingsTilePrivacySaveSearchHistory(),
|
||||||
SettingsTilePrivacyEnableBin(),
|
if (!device.isTelevision) SettingsTilePrivacyEnableBin(),
|
||||||
SettingsTilePrivacyHiddenItems(),
|
SettingsTilePrivacyHiddenItems(),
|
||||||
if (device.canGrantDirectoryAccess) SettingsTilePrivacyStorageAccess(),
|
if (!device.isTelevision && device.canGrantDirectoryAccess) SettingsTilePrivacyStorageAccess(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
import 'package:aves/model/actions/settings_actions.dart';
|
import 'package:aves/model/actions/settings_actions.dart';
|
||||||
|
import 'package:aves/model/device.dart';
|
||||||
import 'package:aves/model/source/collection_source.dart';
|
import 'package:aves/model/source/collection_source.dart';
|
||||||
import 'package:aves/ref/mime_types.dart';
|
import 'package:aves/ref/mime_types.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
|
@ -75,6 +76,7 @@ class _SettingsPageState extends State<SettingsPage> with FeedbackMixin {
|
||||||
onPressed: () => _goToSearch(context),
|
onPressed: () => _goToSearch(context),
|
||||||
tooltip: MaterialLocalizations.of(context).searchFieldLabel,
|
tooltip: MaterialLocalizations.of(context).searchFieldLabel,
|
||||||
),
|
),
|
||||||
|
if (!device.isTelevision)
|
||||||
MenuIconTheme(
|
MenuIconTheme(
|
||||||
child: PopupMenuButton<SettingsAction>(
|
child: PopupMenuButton<SettingsAction>(
|
||||||
itemBuilder: (context) {
|
itemBuilder: (context) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:aves/model/device.dart';
|
||||||
import 'package:aves/model/filters/mime.dart';
|
import 'package:aves/model/filters/mime.dart';
|
||||||
import 'package:aves/model/settings/enums/enums.dart';
|
import 'package:aves/model/settings/enums/enums.dart';
|
||||||
import 'package:aves/model/settings/enums/video_auto_play_mode.dart';
|
import 'package:aves/model/settings/enums/video_auto_play_mode.dart';
|
||||||
|
@ -42,7 +43,7 @@ class VideoSection extends SettingsSection {
|
||||||
SettingsTileVideoEnableHardwareAcceleration(),
|
SettingsTileVideoEnableHardwareAcceleration(),
|
||||||
SettingsTileVideoEnableAutoPlay(),
|
SettingsTileVideoEnableAutoPlay(),
|
||||||
SettingsTileVideoLoopMode(),
|
SettingsTileVideoLoopMode(),
|
||||||
SettingsTileVideoControls(),
|
if (!device.isTelevision) SettingsTileVideoControls(),
|
||||||
SettingsTileVideoSubtitleTheme(),
|
SettingsTileVideoSubtitleTheme(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:aves/model/device.dart';
|
||||||
import 'package:aves/model/settings/enums/enums.dart';
|
import 'package:aves/model/settings/enums/enums.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/services/common/services.dart';
|
import 'package:aves/services/common/services.dart';
|
||||||
|
@ -36,9 +37,9 @@ class ViewerSection extends SettingsSection {
|
||||||
SettingsTileViewerQuickActions(),
|
SettingsTileViewerQuickActions(),
|
||||||
SettingsTileViewerOverlay(),
|
SettingsTileViewerOverlay(),
|
||||||
SettingsTileViewerSlideshow(),
|
SettingsTileViewerSlideshow(),
|
||||||
SettingsTileViewerGestureSideTapNext(),
|
if (!device.isTelevision) SettingsTileViewerGestureSideTapNext(),
|
||||||
if (canSetCutoutMode) SettingsTileViewerCutoutMode(),
|
if (!device.isTelevision && canSetCutoutMode) SettingsTileViewerCutoutMode(),
|
||||||
SettingsTileViewerMaxBrightness(),
|
if (!device.isTelevision) SettingsTileViewerMaxBrightness(),
|
||||||
SettingsTileViewerMotionPhotoAutoPlay(),
|
SettingsTileViewerMotionPhotoAutoPlay(),
|
||||||
SettingsTileViewerImageBackground(),
|
SettingsTileViewerImageBackground(),
|
||||||
];
|
];
|
||||||
|
|
|
@ -72,22 +72,25 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
||||||
return collection != null;
|
return collection != null;
|
||||||
case EntryAction.delete:
|
case EntryAction.delete:
|
||||||
case EntryAction.rename:
|
case EntryAction.rename:
|
||||||
case EntryAction.copy:
|
|
||||||
case EntryAction.move:
|
case EntryAction.move:
|
||||||
return targetEntry.canEdit;
|
return targetEntry.canEdit;
|
||||||
|
case EntryAction.copy:
|
||||||
|
return !device.isReadOnly;
|
||||||
case EntryAction.rotateCCW:
|
case EntryAction.rotateCCW:
|
||||||
case EntryAction.rotateCW:
|
case EntryAction.rotateCW:
|
||||||
return targetEntry.canRotate;
|
return targetEntry.canRotate;
|
||||||
case EntryAction.flip:
|
case EntryAction.flip:
|
||||||
return targetEntry.canFlip;
|
return targetEntry.canFlip;
|
||||||
case EntryAction.convert:
|
case EntryAction.convert:
|
||||||
|
return !device.isReadOnly && !targetEntry.isVideo;
|
||||||
case EntryAction.print:
|
case EntryAction.print:
|
||||||
return !targetEntry.isVideo && device.canPrint;
|
return device.canPrint && !targetEntry.isVideo;
|
||||||
case EntryAction.openMap:
|
case EntryAction.openMap:
|
||||||
return targetEntry.hasGps;
|
return targetEntry.hasGps;
|
||||||
case EntryAction.viewSource:
|
case EntryAction.viewSource:
|
||||||
return targetEntry.isSvg;
|
return targetEntry.isSvg;
|
||||||
case EntryAction.videoCaptureFrame:
|
case EntryAction.videoCaptureFrame:
|
||||||
|
return !device.isReadOnly && targetEntry.isVideo;
|
||||||
case EntryAction.videoToggleMute:
|
case EntryAction.videoToggleMute:
|
||||||
case EntryAction.videoSelectStreams:
|
case EntryAction.videoSelectStreams:
|
||||||
case EntryAction.videoSetSpeed:
|
case EntryAction.videoSetSpeed:
|
||||||
|
@ -98,12 +101,13 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
||||||
case EntryAction.openVideo:
|
case EntryAction.openVideo:
|
||||||
return targetEntry.isVideo;
|
return targetEntry.isVideo;
|
||||||
case EntryAction.rotateScreen:
|
case EntryAction.rotateScreen:
|
||||||
return settings.isRotationLocked;
|
return !device.isTelevision && settings.isRotationLocked;
|
||||||
case EntryAction.addShortcut:
|
case EntryAction.addShortcut:
|
||||||
return device.canPinShortcut;
|
return device.canPinShortcut;
|
||||||
|
case EntryAction.edit:
|
||||||
|
return !device.isReadOnly;
|
||||||
case EntryAction.info:
|
case EntryAction.info:
|
||||||
case EntryAction.copyToClipboard:
|
case EntryAction.copyToClipboard:
|
||||||
case EntryAction.edit:
|
|
||||||
case EntryAction.open:
|
case EntryAction.open:
|
||||||
case EntryAction.setAs:
|
case EntryAction.setAs:
|
||||||
case EntryAction.share:
|
case EntryAction.share:
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:convert';
|
||||||
|
|
||||||
import 'package:aves/model/actions/entry_actions.dart';
|
import 'package:aves/model/actions/entry_actions.dart';
|
||||||
import 'package:aves/model/actions/events.dart';
|
import 'package:aves/model/actions/events.dart';
|
||||||
|
import 'package:aves/model/device.dart';
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
import 'package:aves/model/entry_info.dart';
|
import 'package:aves/model/entry_info.dart';
|
||||||
import 'package:aves/model/entry_metadata_edition.dart';
|
import 'package:aves/model/entry_metadata_edition.dart';
|
||||||
|
@ -37,12 +38,13 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi
|
||||||
case EntryAction.editTags:
|
case EntryAction.editTags:
|
||||||
case EntryAction.removeMetadata:
|
case EntryAction.removeMetadata:
|
||||||
case EntryAction.exportMetadata:
|
case EntryAction.exportMetadata:
|
||||||
return true;
|
return !device.isReadOnly;
|
||||||
// GeoTIFF
|
// GeoTIFF
|
||||||
case EntryAction.showGeoTiffOnMap:
|
case EntryAction.showGeoTiffOnMap:
|
||||||
return targetEntry.isGeotiff;
|
return targetEntry.isGeotiff;
|
||||||
// motion photo
|
// motion photo
|
||||||
case EntryAction.convertMotionPhotoToStillImage:
|
case EntryAction.convertMotionPhotoToStillImage:
|
||||||
|
return !device.isReadOnly && targetEntry.isMotionPhoto;
|
||||||
case EntryAction.viewMotionPhotoVideo:
|
case EntryAction.viewMotionPhotoVideo:
|
||||||
return targetEntry.isMotionPhoto;
|
return targetEntry.isMotionPhoto;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -1,12 +1,16 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:aves/model/entry.dart';
|
import 'package:aves/model/entry.dart';
|
||||||
|
import 'package:aves/model/settings/enums/enums.dart';
|
||||||
|
import 'package:aves/model/settings/settings.dart';
|
||||||
|
import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/widgets/viewer/video/controller.dart';
|
import 'package:aves/widgets/viewer/video/controller.dart';
|
||||||
import 'package:aves/widgets/viewer/video/fijkplayer.dart';
|
import 'package:aves/widgets/viewer/video/fijkplayer.dart';
|
||||||
import 'package:collection/collection.dart';
|
import 'package:collection/collection.dart';
|
||||||
|
|
||||||
class VideoConductor {
|
class VideoConductor {
|
||||||
final List<AvesVideoController> _controllers = [];
|
final List<AvesVideoController> _controllers = [];
|
||||||
|
final List<StreamSubscription> _subscriptions = [];
|
||||||
final bool persistPlayback;
|
final bool persistPlayback;
|
||||||
|
|
||||||
static const _defaultMaxControllerCount = 3;
|
static const _defaultMaxControllerCount = 3;
|
||||||
|
@ -15,7 +19,13 @@ class VideoConductor {
|
||||||
|
|
||||||
Future<void> dispose() async {
|
Future<void> dispose() async {
|
||||||
await Future.forEach<AvesVideoController>(_controllers, (controller) => controller.dispose());
|
await Future.forEach<AvesVideoController>(_controllers, (controller) => controller.dispose());
|
||||||
|
_subscriptions
|
||||||
|
..forEach((sub) => sub.cancel())
|
||||||
|
..clear();
|
||||||
_controllers.clear();
|
_controllers.clear();
|
||||||
|
if (settings.keepScreenOn == KeepScreenOn.videoPlayback) {
|
||||||
|
await windowService.keepScreenOn(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AvesVideoController getOrCreateController(AvesEntry entry, {int? maxControllerCount}) {
|
AvesVideoController getOrCreateController(AvesEntry entry, {int? maxControllerCount}) {
|
||||||
|
@ -24,6 +34,7 @@ class VideoConductor {
|
||||||
_controllers.remove(controller);
|
_controllers.remove(controller);
|
||||||
} else {
|
} else {
|
||||||
controller = IjkPlayerAvesVideoController(entry, persistPlayback: persistPlayback);
|
controller = IjkPlayerAvesVideoController(entry, persistPlayback: persistPlayback);
|
||||||
|
_subscriptions.add(controller.statusStream.listen(_onControllerStatusChanged));
|
||||||
}
|
}
|
||||||
_controllers.insert(0, controller);
|
_controllers.insert(0, controller);
|
||||||
while (_controllers.length > (maxControllerCount ?? _defaultMaxControllerCount)) {
|
while (_controllers.length > (maxControllerCount ?? _defaultMaxControllerCount)) {
|
||||||
|
@ -36,6 +47,12 @@ class VideoConductor {
|
||||||
return _controllers.firstWhereOrNull((c) => c.entry.uri == entry.uri && c.entry.pageId == entry.pageId);
|
return _controllers.firstWhereOrNull((c) => c.entry.uri == entry.uri && c.entry.pageId == entry.pageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _onControllerStatusChanged(VideoStatus status) async {
|
||||||
|
if (settings.keepScreenOn == KeepScreenOn.videoPlayback) {
|
||||||
|
await windowService.keepScreenOn(status == VideoStatus.playing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _applyToAll(FutureOr Function(AvesVideoController controller) action) => Future.forEach<AvesVideoController>(_controllers, action);
|
Future<void> _applyToAll(FutureOr Function(AvesVideoController controller) action) => Future.forEach<AvesVideoController>(_controllers, action);
|
||||||
|
|
||||||
Future<void> pauseAll() => _applyToAll((controller) => controller.pause());
|
Future<void> pauseAll() => _applyToAll((controller) => controller.pause());
|
||||||
|
|
|
@ -138,6 +138,7 @@
|
||||||
"nameConflictStrategyReplace",
|
"nameConflictStrategyReplace",
|
||||||
"nameConflictStrategySkip",
|
"nameConflictStrategySkip",
|
||||||
"keepScreenOnNever",
|
"keepScreenOnNever",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"keepScreenOnViewerOnly",
|
"keepScreenOnViewerOnly",
|
||||||
"keepScreenOnAlways",
|
"keepScreenOnAlways",
|
||||||
"accessibilityAnimationsRemove",
|
"accessibilityAnimationsRemove",
|
||||||
|
@ -599,7 +600,8 @@
|
||||||
"de": [
|
"de": [
|
||||||
"entryActionShareImageOnly",
|
"entryActionShareImageOnly",
|
||||||
"entryActionShareVideoOnly",
|
"entryActionShareVideoOnly",
|
||||||
"entryInfoActionRemoveLocation"
|
"entryInfoActionRemoveLocation",
|
||||||
|
"keepScreenOnVideoPlayback"
|
||||||
],
|
],
|
||||||
|
|
||||||
"el": [
|
"el": [
|
||||||
|
@ -609,13 +611,15 @@
|
||||||
"filterAspectRatioLandscapeLabel",
|
"filterAspectRatioLandscapeLabel",
|
||||||
"filterAspectRatioPortraitLabel",
|
"filterAspectRatioPortraitLabel",
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"settingsViewerShowRatingTags"
|
"settingsViewerShowRatingTags"
|
||||||
],
|
],
|
||||||
|
|
||||||
"es": [
|
"es": [
|
||||||
"entryActionShareImageOnly",
|
"entryActionShareImageOnly",
|
||||||
"entryActionShareVideoOnly",
|
"entryActionShareVideoOnly",
|
||||||
"entryInfoActionRemoveLocation"
|
"entryInfoActionRemoveLocation",
|
||||||
|
"keepScreenOnVideoPlayback"
|
||||||
],
|
],
|
||||||
|
|
||||||
"fa": [
|
"fa": [
|
||||||
|
@ -757,6 +761,7 @@
|
||||||
"nameConflictStrategyReplace",
|
"nameConflictStrategyReplace",
|
||||||
"nameConflictStrategySkip",
|
"nameConflictStrategySkip",
|
||||||
"keepScreenOnNever",
|
"keepScreenOnNever",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"keepScreenOnViewerOnly",
|
"keepScreenOnViewerOnly",
|
||||||
"keepScreenOnAlways",
|
"keepScreenOnAlways",
|
||||||
"accessibilityAnimationsRemove",
|
"accessibilityAnimationsRemove",
|
||||||
|
@ -1218,7 +1223,8 @@
|
||||||
"fr": [
|
"fr": [
|
||||||
"entryActionShareImageOnly",
|
"entryActionShareImageOnly",
|
||||||
"entryActionShareVideoOnly",
|
"entryActionShareVideoOnly",
|
||||||
"entryInfoActionRemoveLocation"
|
"entryInfoActionRemoveLocation",
|
||||||
|
"keepScreenOnVideoPlayback"
|
||||||
],
|
],
|
||||||
|
|
||||||
"gl": [
|
"gl": [
|
||||||
|
@ -1229,6 +1235,7 @@
|
||||||
"filterAspectRatioLandscapeLabel",
|
"filterAspectRatioLandscapeLabel",
|
||||||
"filterAspectRatioPortraitLabel",
|
"filterAspectRatioPortraitLabel",
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"accessibilityAnimationsRemove",
|
"accessibilityAnimationsRemove",
|
||||||
"accessibilityAnimationsKeep",
|
"accessibilityAnimationsKeep",
|
||||||
"displayRefreshRatePreferHighest",
|
"displayRefreshRatePreferHighest",
|
||||||
|
@ -1693,6 +1700,7 @@
|
||||||
"filterAspectRatioLandscapeLabel",
|
"filterAspectRatioLandscapeLabel",
|
||||||
"filterAspectRatioPortraitLabel",
|
"filterAspectRatioPortraitLabel",
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"subtitlePositionTop",
|
"subtitlePositionTop",
|
||||||
"subtitlePositionBottom",
|
"subtitlePositionBottom",
|
||||||
"widgetDisplayedItemRandom",
|
"widgetDisplayedItemRandom",
|
||||||
|
@ -1710,6 +1718,7 @@
|
||||||
"filterAspectRatioLandscapeLabel",
|
"filterAspectRatioLandscapeLabel",
|
||||||
"filterAspectRatioPortraitLabel",
|
"filterAspectRatioPortraitLabel",
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"settingsViewerShowRatingTags"
|
"settingsViewerShowRatingTags"
|
||||||
],
|
],
|
||||||
|
|
||||||
|
@ -1722,6 +1731,7 @@
|
||||||
"filterAspectRatioLandscapeLabel",
|
"filterAspectRatioLandscapeLabel",
|
||||||
"filterAspectRatioPortraitLabel",
|
"filterAspectRatioPortraitLabel",
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"subtitlePositionTop",
|
"subtitlePositionTop",
|
||||||
"subtitlePositionBottom",
|
"subtitlePositionBottom",
|
||||||
"widgetDisplayedItemRandom",
|
"widgetDisplayedItemRandom",
|
||||||
|
@ -1735,19 +1745,22 @@
|
||||||
"ko": [
|
"ko": [
|
||||||
"entryActionShareImageOnly",
|
"entryActionShareImageOnly",
|
||||||
"entryActionShareVideoOnly",
|
"entryActionShareVideoOnly",
|
||||||
"entryInfoActionRemoveLocation"
|
"entryInfoActionRemoveLocation",
|
||||||
|
"keepScreenOnVideoPlayback"
|
||||||
],
|
],
|
||||||
|
|
||||||
"lt": [
|
"lt": [
|
||||||
"entryActionShareImageOnly",
|
"entryActionShareImageOnly",
|
||||||
"entryActionShareVideoOnly",
|
"entryActionShareVideoOnly",
|
||||||
"entryInfoActionRemoveLocation"
|
"entryInfoActionRemoveLocation",
|
||||||
|
"keepScreenOnVideoPlayback"
|
||||||
],
|
],
|
||||||
|
|
||||||
"nb": [
|
"nb": [
|
||||||
"entryActionShareImageOnly",
|
"entryActionShareImageOnly",
|
||||||
"entryActionShareVideoOnly",
|
"entryActionShareVideoOnly",
|
||||||
"entryInfoActionRemoveLocation"
|
"entryInfoActionRemoveLocation",
|
||||||
|
"keepScreenOnVideoPlayback"
|
||||||
],
|
],
|
||||||
|
|
||||||
"nl": [
|
"nl": [
|
||||||
|
@ -1758,6 +1771,7 @@
|
||||||
"filterAspectRatioLandscapeLabel",
|
"filterAspectRatioLandscapeLabel",
|
||||||
"filterAspectRatioPortraitLabel",
|
"filterAspectRatioPortraitLabel",
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"subtitlePositionTop",
|
"subtitlePositionTop",
|
||||||
"subtitlePositionBottom",
|
"subtitlePositionBottom",
|
||||||
"widgetDisplayedItemRandom",
|
"widgetDisplayedItemRandom",
|
||||||
|
@ -1781,6 +1795,7 @@
|
||||||
"filterTypePanoramaLabel",
|
"filterTypePanoramaLabel",
|
||||||
"mapStyleOsmHot",
|
"mapStyleOsmHot",
|
||||||
"mapStyleStamenToner",
|
"mapStyleStamenToner",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"accessibilityAnimationsKeep",
|
"accessibilityAnimationsKeep",
|
||||||
"displayRefreshRatePreferHighest",
|
"displayRefreshRatePreferHighest",
|
||||||
"displayRefreshRatePreferLowest",
|
"displayRefreshRatePreferLowest",
|
||||||
|
@ -2261,6 +2276,7 @@
|
||||||
"nameConflictStrategyReplace",
|
"nameConflictStrategyReplace",
|
||||||
"nameConflictStrategySkip",
|
"nameConflictStrategySkip",
|
||||||
"keepScreenOnNever",
|
"keepScreenOnNever",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"keepScreenOnViewerOnly",
|
"keepScreenOnViewerOnly",
|
||||||
"keepScreenOnAlways",
|
"keepScreenOnAlways",
|
||||||
"accessibilityAnimationsRemove",
|
"accessibilityAnimationsRemove",
|
||||||
|
@ -2727,6 +2743,7 @@
|
||||||
"filterAspectRatioLandscapeLabel",
|
"filterAspectRatioLandscapeLabel",
|
||||||
"filterAspectRatioPortraitLabel",
|
"filterAspectRatioPortraitLabel",
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"subtitlePositionTop",
|
"subtitlePositionTop",
|
||||||
"subtitlePositionBottom",
|
"subtitlePositionBottom",
|
||||||
"widgetDisplayedItemRandom",
|
"widgetDisplayedItemRandom",
|
||||||
|
@ -2744,13 +2761,15 @@
|
||||||
"filterAspectRatioLandscapeLabel",
|
"filterAspectRatioLandscapeLabel",
|
||||||
"filterAspectRatioPortraitLabel",
|
"filterAspectRatioPortraitLabel",
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"settingsViewerShowRatingTags"
|
"settingsViewerShowRatingTags"
|
||||||
],
|
],
|
||||||
|
|
||||||
"ru": [
|
"ru": [
|
||||||
"entryActionShareImageOnly",
|
"entryActionShareImageOnly",
|
||||||
"entryActionShareVideoOnly",
|
"entryActionShareVideoOnly",
|
||||||
"entryInfoActionRemoveLocation"
|
"entryInfoActionRemoveLocation",
|
||||||
|
"keepScreenOnVideoPlayback"
|
||||||
],
|
],
|
||||||
|
|
||||||
"th": [
|
"th": [
|
||||||
|
@ -2769,6 +2788,7 @@
|
||||||
"filterAspectRatioPortraitLabel",
|
"filterAspectRatioPortraitLabel",
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
"coordinateDms",
|
"coordinateDms",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"keepScreenOnViewerOnly",
|
"keepScreenOnViewerOnly",
|
||||||
"accessibilityAnimationsRemove",
|
"accessibilityAnimationsRemove",
|
||||||
"accessibilityAnimationsKeep",
|
"accessibilityAnimationsKeep",
|
||||||
|
@ -3133,7 +3153,8 @@
|
||||||
"tr": [
|
"tr": [
|
||||||
"entryActionShareImageOnly",
|
"entryActionShareImageOnly",
|
||||||
"entryActionShareVideoOnly",
|
"entryActionShareVideoOnly",
|
||||||
"entryInfoActionRemoveLocation"
|
"entryInfoActionRemoveLocation",
|
||||||
|
"keepScreenOnVideoPlayback"
|
||||||
],
|
],
|
||||||
|
|
||||||
"zh": [
|
"zh": [
|
||||||
|
@ -3143,6 +3164,7 @@
|
||||||
"filterAspectRatioLandscapeLabel",
|
"filterAspectRatioLandscapeLabel",
|
||||||
"filterAspectRatioPortraitLabel",
|
"filterAspectRatioPortraitLabel",
|
||||||
"filterNoAddressLabel",
|
"filterNoAddressLabel",
|
||||||
|
"keepScreenOnVideoPlayback",
|
||||||
"settingsViewerShowRatingTags"
|
"settingsViewerShowRatingTags"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue