mirror of
https://github.com/immich-app/immich.git
synced 2025-12-24 01:11:32 +03:00
feat(mobile): add cast support (#18341)
* initial cast framework complete and mocked cast dialog working * wip casting * casting works! just need to add session key check and remote video controls * cleanup of classes * add session expiration checks * cast dialog now shows connected device at top of list with a list header. Discovered devices are also cached for app session. * cast video player finalized * show fullsize assets on casting * translation already happens on the text element * remove prints * fix lintings * code review changes from @shenlong-tanwen * fix connect method override * fix alphabetization * remove important * filter chromecast audio devices * fix some disconnect command ordering issues and unawaited futures * remove prints * only disconnect if we are connected * don't try to reconnect if its the current device * add cast button to top bar * format sessions api * more formatting issues fixed * add snack bar to tell user that we cannot cast an asset that is not uploaded to server * make casting icon change to primary color when casting is active * only show casting snackbar if we are casting * dont show cast button if asset is remote and we are not casting * stop playing media if we seek to an asset that is not remote * remove https check since it works with local http IP addresses * remove unneeded imports * fix recasting when socket closes * fix info plist formatting * only show cast button if there is an active websocket connection (ie the server is accessible) * add device capability bitmask checks * small comment about bitmask
This commit is contained in:
@@ -4,6 +4,7 @@ import 'dart:math';
|
||||
import 'dart:ui' as ui;
|
||||
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart' hide Store;
|
||||
@@ -20,6 +21,7 @@ import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart
|
||||
import 'package:immich_mobile/providers/asset_viewer/is_motion_video_playing.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/show_controls.provider.dart';
|
||||
import 'package:immich_mobile/providers/asset_viewer/video_player_value_provider.dart';
|
||||
import 'package:immich_mobile/providers/cast.provider.dart';
|
||||
import 'package:immich_mobile/providers/haptic_feedback.provider.dart';
|
||||
import 'package:immich_mobile/services/app_settings.service.dart';
|
||||
import 'package:immich_mobile/widgets/asset_grid/asset_grid_data_structure.dart';
|
||||
@@ -62,6 +64,7 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||
final currentIndex = useValueNotifier(initialIndex);
|
||||
final loadAsset = renderList.loadAsset;
|
||||
final isPlayingMotionVideo = ref.watch(isPlayingMotionVideoProvider);
|
||||
final isCasting = ref.watch(castProvider.select((c) => c.isCasting));
|
||||
|
||||
final videoPlayerKeys = useRef<Map<int, GlobalKey>>({});
|
||||
|
||||
@@ -118,6 +121,36 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||
const [],
|
||||
);
|
||||
|
||||
useEffect(() {
|
||||
final asset = loadAsset(currentIndex.value);
|
||||
|
||||
if (asset.isRemote) {
|
||||
ref.read(castProvider.notifier).loadMedia(asset, false);
|
||||
} else {
|
||||
if (isCasting) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (context.mounted) {
|
||||
ref.read(castProvider.notifier).stop();
|
||||
context.scaffoldMessenger.showSnackBar(
|
||||
SnackBar(
|
||||
duration: const Duration(seconds: 1),
|
||||
content: Text(
|
||||
"local_asset_cast_failed".tr(),
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}, [
|
||||
ref.watch(castProvider).isCasting,
|
||||
]);
|
||||
|
||||
void showInfo() {
|
||||
final asset = ref.read(currentAssetProvider);
|
||||
if (asset == null) {
|
||||
@@ -356,6 +389,30 @@ class GalleryViewerPage extends HookConsumerWidget {
|
||||
Timer(const Duration(milliseconds: 400), () {
|
||||
precacheNextImage(next);
|
||||
});
|
||||
|
||||
context.scaffoldMessenger.hideCurrentSnackBar();
|
||||
|
||||
// send image to casting if the server has it
|
||||
if (newAsset.isRemote) {
|
||||
ref.read(castProvider.notifier).loadMedia(newAsset, false);
|
||||
} else {
|
||||
context.scaffoldMessenger.clearSnackBars();
|
||||
|
||||
if (isCasting) {
|
||||
ref.read(castProvider.notifier).stop();
|
||||
context.scaffoldMessenger.showSnackBar(
|
||||
SnackBar(
|
||||
duration: const Duration(seconds: 2),
|
||||
content: Text(
|
||||
"local_asset_cast_failed".tr(),
|
||||
style: context.textTheme.bodyLarge?.copyWith(
|
||||
color: context.primaryColor,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
builder: buildAsset,
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user