214 lines
5.6 KiB
Dart
214 lines
5.6 KiB
Dart
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;
|
|
}
|
|
|
|
if (defaultTargetPlatform == TargetPlatform.android) {
|
|
_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();
|
|
}
|
|
}
|