Adds Apple airplay/Google cast support to the app
This commit is contained in:
@@ -0,0 +1,211 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
enum CastingStatus { unavailable, idle, connecting, connected, error }
|
||||
|
||||
enum CastingPlaybackStatus { unknown, stopped, buffering, playing }
|
||||
|
||||
class CastingBusinessObject extends ChangeNotifier {
|
||||
static const MethodChannel _methodChannel = MethodChannel(
|
||||
'kryz_go_flutter/casting/methods',
|
||||
);
|
||||
static const EventChannel _eventChannel = EventChannel(
|
||||
'kryz_go_flutter/casting/events',
|
||||
);
|
||||
|
||||
StreamSubscription<dynamic>? _eventSubscription;
|
||||
CastingStatus _status = CastingStatus.unavailable;
|
||||
CastingPlaybackStatus _playbackStatus = CastingPlaybackStatus.unknown;
|
||||
String? _connectedDeviceName;
|
||||
String? _lastError;
|
||||
|
||||
CastingStatus get status => _status;
|
||||
CastingPlaybackStatus get playbackStatus => _playbackStatus;
|
||||
String? get connectedDeviceName => _connectedDeviceName;
|
||||
String? get lastError => _lastError;
|
||||
|
||||
bool get isSupportedOnThisPlatform {
|
||||
if (kIsWeb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return defaultTargetPlatform == TargetPlatform.iOS ||
|
||||
defaultTargetPlatform == TargetPlatform.android;
|
||||
}
|
||||
|
||||
bool get isConnected => _status == CastingStatus.connected;
|
||||
bool get isPlaying => _playbackStatus == CastingPlaybackStatus.playing;
|
||||
bool get isBuffering => _playbackStatus == CastingPlaybackStatus.buffering;
|
||||
bool get isStopped => _playbackStatus == CastingPlaybackStatus.stopped;
|
||||
|
||||
Future<void> initialize() async {
|
||||
if (!isSupportedOnThisPlatform) {
|
||||
_setStatus(CastingStatus.unavailable);
|
||||
return;
|
||||
}
|
||||
|
||||
_eventSubscription?.cancel();
|
||||
_eventSubscription = _eventChannel.receiveBroadcastStream().listen(
|
||||
_handleEvent,
|
||||
onError: (Object error) {
|
||||
_lastError = error.toString();
|
||||
_setStatus(CastingStatus.error);
|
||||
},
|
||||
);
|
||||
|
||||
try {
|
||||
final dynamic status = await _methodChannel.invokeMethod<dynamic>(
|
||||
'getStatus',
|
||||
);
|
||||
_handleEvent(status);
|
||||
} on PlatformException catch (error) {
|
||||
_lastError = error.message ?? error.code;
|
||||
_setStatus(CastingStatus.error);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> showDevicePicker() async {
|
||||
if (!isSupportedOnThisPlatform) {
|
||||
_setStatus(CastingStatus.unavailable);
|
||||
return;
|
||||
}
|
||||
|
||||
_setStatus(CastingStatus.connecting);
|
||||
|
||||
try {
|
||||
await _methodChannel.invokeMethod<void>('showDevicePicker');
|
||||
} on PlatformException catch (error) {
|
||||
_lastError = error.message ?? error.code;
|
||||
_setStatus(CastingStatus.error);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> startCastingLiveStream({
|
||||
required String streamUrl,
|
||||
required String title,
|
||||
required String subtitle,
|
||||
}) async {
|
||||
if (!isSupportedOnThisPlatform) {
|
||||
_setStatus(CastingStatus.unavailable);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await _methodChannel.invokeMethod<void>('startCastingLiveStream', {
|
||||
'streamUrl': streamUrl,
|
||||
'title': title,
|
||||
'subtitle': subtitle,
|
||||
});
|
||||
} on PlatformException catch (error) {
|
||||
_lastError = error.message ?? error.code;
|
||||
_setStatus(CastingStatus.error);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> disconnect() async {
|
||||
if (!isSupportedOnThisPlatform) {
|
||||
_setStatus(CastingStatus.unavailable);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await _methodChannel.invokeMethod<void>('disconnect');
|
||||
} on PlatformException catch (error) {
|
||||
_lastError = error.message ?? error.code;
|
||||
_setStatus(CastingStatus.error);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stopPlayback() async {
|
||||
if (!isSupportedOnThisPlatform) {
|
||||
_setStatus(CastingStatus.unavailable);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await _methodChannel.invokeMethod<void>('stopCastingPlayback');
|
||||
} on PlatformException catch (error) {
|
||||
_lastError = error.message ?? error.code;
|
||||
_setStatus(CastingStatus.error);
|
||||
}
|
||||
}
|
||||
|
||||
void _handleEvent(dynamic event) {
|
||||
if (event is! Map) {
|
||||
return;
|
||||
}
|
||||
|
||||
final String? status = event['status'] as String?;
|
||||
final String? playbackStatus = event['playbackStatus'] as String?;
|
||||
final String? deviceName = event['deviceName'] as String?;
|
||||
final String? message = event['message'] as String?;
|
||||
|
||||
if (deviceName != null && deviceName.isNotEmpty) {
|
||||
_connectedDeviceName = deviceName;
|
||||
}
|
||||
|
||||
if (message != null && message.isNotEmpty) {
|
||||
_lastError = message;
|
||||
}
|
||||
|
||||
_setPlaybackStatus(_parsePlaybackStatus(playbackStatus));
|
||||
|
||||
switch (status) {
|
||||
case 'idle':
|
||||
_setStatus(CastingStatus.idle);
|
||||
break;
|
||||
case 'connecting':
|
||||
_setStatus(CastingStatus.connecting);
|
||||
break;
|
||||
case 'connected':
|
||||
_setStatus(CastingStatus.connected);
|
||||
break;
|
||||
case 'error':
|
||||
_setStatus(CastingStatus.error);
|
||||
break;
|
||||
case 'unavailable':
|
||||
default:
|
||||
_setStatus(CastingStatus.unavailable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _setStatus(CastingStatus status) {
|
||||
if (_status == status) {
|
||||
return;
|
||||
}
|
||||
|
||||
_status = status;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
CastingPlaybackStatus _parsePlaybackStatus(String? playbackStatus) {
|
||||
switch (playbackStatus) {
|
||||
case 'playing':
|
||||
return CastingPlaybackStatus.playing;
|
||||
case 'buffering':
|
||||
return CastingPlaybackStatus.buffering;
|
||||
case 'stopped':
|
||||
return CastingPlaybackStatus.stopped;
|
||||
default:
|
||||
return CastingPlaybackStatus.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
void _setPlaybackStatus(CastingPlaybackStatus status) {
|
||||
if (_playbackStatus == status) {
|
||||
return;
|
||||
}
|
||||
|
||||
_playbackStatus = status;
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_eventSubscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user