Line data Source code
1 : import 'dart:convert';
2 :
3 : import 'package:flutter/services.dart';
4 : import 'package:flutter_chrome_cast/_remote_media_client/remote_media_client_platform.dart';
5 : import 'package:flutter_chrome_cast/entities/cast_media_status.dart';
6 : import 'package:flutter_chrome_cast/entities/load_options.dart';
7 : import 'package:flutter_chrome_cast/entities/media_information.dart';
8 : import 'package:flutter_chrome_cast/entities/queue_item.dart';
9 : import 'package:flutter_chrome_cast/entities/media_seek_option.dart';
10 : import 'package:flutter_chrome_cast/entities/request.dart';
11 : import 'package:flutter_chrome_cast/models/android/android_media_status.dart';
12 : import 'package:flutter_chrome_cast/models/android/android_queue_item.dart';
13 : import 'package:rxdart/subjects.dart';
14 :
15 : /// Android-specific implementation of Google Cast remote media client functionality.
16 : class GoogleCastRemoteMediaClientAndroidMethodChannel
17 : implements GoogleCastRemoteMediaClientPlatformInterface {
18 : /// Creates a new Android remote media client method channel.
19 1 : GoogleCastRemoteMediaClientAndroidMethodChannel() {
20 3 : _channel.setMethodCallHandler(_onMethodCallHandler);
21 : }
22 :
23 : final _channel =
24 : const MethodChannel('com.felnanuke.google_cast.remote_media_client');
25 :
26 : // Media Status
27 : final _mediaStatusStreamController = BehaviorSubject<GoggleCastMediaStatus?>()
28 : ..add(null);
29 :
30 1 : @override
31 2 : GoggleCastMediaStatus? get mediaStatus => _mediaStatusStreamController.value;
32 :
33 1 : @override
34 : Stream<GoggleCastMediaStatus?> get mediaStatusStream =>
35 2 : _mediaStatusStreamController.stream;
36 :
37 : // QueueItems
38 : final _queueItemsStreamController =
39 : BehaviorSubject<List<GoogleCastQueueItem>>()..add([]);
40 :
41 1 : @override
42 2 : List<GoogleCastQueueItem> get queueItems => _queueItemsStreamController.value;
43 :
44 1 : @override
45 : Stream<List<GoogleCastQueueItem>> get queueItemsStream =>
46 2 : _queueItemsStreamController.stream;
47 :
48 : // PlayerPosition
49 : final _playerPositionStreamController = BehaviorSubject<Duration>()
50 : ..add(Duration.zero);
51 1 : @override
52 2 : Duration get playerPosition => _playerPositionStreamController.value;
53 :
54 1 : @override
55 : Stream<Duration> get playerPositionStream =>
56 2 : _playerPositionStreamController.stream;
57 :
58 : // Queue has Next or revious item
59 1 : @override
60 : bool get queueHasNextItem {
61 2 : final currentQueueItemId = mediaStatus?.currentItemId;
62 1 : final currentItemIndex = queueItems
63 3 : .map((e) => e.itemId)
64 1 : .toList()
65 1 : .lastIndexOf(currentQueueItemId);
66 4 : return (queueItems.length - 1) > currentItemIndex;
67 : }
68 :
69 1 : @override
70 : bool get queueHasPreviousItem => true;
71 :
72 1 : @override
73 : Future<GoogleCastRequest?> loadMedia(
74 : GoogleCastMediaInformation mediaInfo, {
75 : bool autoPlay = true,
76 : Duration playPosition = Duration.zero,
77 : double playbackRate = 1.0,
78 : List<int>? activeTrackIds,
79 : String? credentials,
80 : String? credentialsType,
81 : }) async {
82 2 : final result = await _channel.invokeMethod(
83 : 'loadMedia',
84 1 : {
85 1 : 'mediaInfo': mediaInfo.toMap(),
86 : 'autoPlay': autoPlay,
87 1 : 'playPosition': playPosition.inSeconds,
88 : 'playbackRate': playbackRate,
89 : 'activeTrackIds': activeTrackIds,
90 : 'credentials': credentials,
91 : 'credentialsType': credentialsType
92 : },
93 : );
94 : return result != null
95 2 : ? GoogleCastRequest.fromMap(Map<String, dynamic>.from(result))
96 : : null;
97 : }
98 :
99 1 : @override
100 : Future<GoogleCastRequest> pause() async {
101 2 : final result = await _channel.invokeMethod('pause');
102 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
103 : }
104 :
105 1 : @override
106 : Future<GoogleCastRequest> play() async {
107 2 : final result = await _channel.invokeMethod('play');
108 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
109 : }
110 :
111 1 : @override
112 : Future<GoogleCastRequest?> queueLoadItems(
113 : List<GoogleCastQueueItem> queueItems, {
114 : GoogleCastQueueLoadOptions? options,
115 : }) async {
116 3 : final result = await _channel.invokeMethod('queueLoadItems', {
117 4 : 'queueItems': queueItems.map((item) => item.toMap()).toList(),
118 1 : 'options': options?.toMap(),
119 : });
120 : return result != null
121 2 : ? GoogleCastRequest.fromMap(Map<String, dynamic>.from(result))
122 : : null;
123 : }
124 :
125 1 : @override
126 : Future<GoogleCastRequest> queueNextItem() async {
127 2 : final result = await _channel.invokeMethod('queueNextItem');
128 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
129 : }
130 :
131 1 : @override
132 : Future<GoogleCastRequest> queuePrevItem() async {
133 2 : final result = await _channel.invokeMethod('queuePrevItem');
134 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
135 : }
136 :
137 1 : @override
138 : Future<GoogleCastRequest> seek(GoogleCastMediaSeekOption option) async {
139 2 : final result = await _channel.invokeMethod(
140 : 'seek',
141 1 : option.toMap(),
142 : );
143 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
144 : }
145 :
146 1 : @override
147 : Future<GoogleCastRequest> setActiveTrackIDs(List<int> activeTrackIDs) async {
148 2 : final result = await _channel.invokeMethod(
149 : 'setActiveTrackIds',
150 : activeTrackIDs,
151 : );
152 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
153 : }
154 :
155 1 : @override
156 : Future<GoogleCastRequest> setPlaybackRate(double rate) async {
157 2 : final result = await _channel.invokeMethod('setPlaybackRate', rate);
158 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
159 : }
160 :
161 1 : @override
162 : Future<GoogleCastRequest> setTextTrackStyle(
163 : TextTrackStyle textTrackStyle) async {
164 2 : final result = await _channel.invokeMethod(
165 : 'setTextTrackStyle',
166 1 : textTrackStyle.toMap(),
167 : );
168 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
169 : }
170 :
171 1 : @override
172 : Future<GoogleCastRequest> stop() async {
173 2 : final result = await _channel.invokeMethod('stop');
174 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
175 : }
176 :
177 1 : @override
178 : Future<GoogleCastRequest> queueJumpToItemWithId(int itemId) async {
179 2 : final result = await _channel.invokeMethod('queueJumpToItemWithId', itemId);
180 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
181 : }
182 :
183 1 : @override
184 : Future<GoogleCastRequest> queueRemoveItemsWithIds(List<int> itemIds) async {
185 : final result =
186 2 : await _channel.invokeMethod('queueRemoveItemsWithIds', itemIds);
187 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
188 : }
189 :
190 1 : @override
191 : Future<GoogleCastRequest> queueInsertItemAndPlay(GoogleCastQueueItem item,
192 : {required int beforeItemWithId}) async {
193 3 : final result = await _channel.invokeMethod('queueInsertItemAndPlay', {
194 1 : 'item': item.toMap(),
195 : 'beforeItemWithId': beforeItemWithId,
196 : });
197 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
198 : }
199 :
200 1 : @override
201 : Future<GoogleCastRequest> queueInsertItems(List<GoogleCastQueueItem> items,
202 : {int? beforeItemWithId}) async {
203 3 : final result = await _channel.invokeMethod('queueInsertItems', {
204 4 : 'items': items.map((item) => item.toMap()).toList(),
205 : 'beforeItemWithId': beforeItemWithId,
206 : });
207 2 : return GoogleCastRequest.fromMap(Map<String, dynamic>.from(result));
208 : }
209 :
210 1 : Future _onMethodCallHandler(MethodCall call) async {
211 1 : switch (call.method) {
212 1 : case 'onMediaStatusChanged':
213 2 : return _onMediaStatusChanged(call.arguments);
214 1 : case 'onQueueStatusChanged':
215 2 : return _onQueueStatusChanged(call.arguments);
216 1 : case 'onPlayerPositionChanged':
217 2 : return _onPlayerPositionChanged(call.arguments);
218 : default:
219 : }
220 : }
221 :
222 1 : Future<void> _onMediaStatusChanged(dynamic arguments) async {
223 : if (arguments == null) {
224 2 : _mediaStatusStreamController.add(null);
225 : return;
226 : }
227 1 : final mediaStatus = GoogleCastAndroidMediaStatus.fromMap(
228 2 : Map<String, dynamic>.from(jsonDecode(arguments)),
229 : );
230 :
231 2 : _mediaStatusStreamController.add(mediaStatus);
232 : }
233 :
234 1 : Future<void> _onQueueStatusChanged(dynamic arguments) async {
235 : if (arguments == null) {
236 3 : _queueItemsStreamController.add([]);
237 : return;
238 : }
239 :
240 1 : final map = List.from(arguments);
241 :
242 : final queueItems = map
243 3 : .map((e) => GoogleCastAndroidQueueItem.fromMap(
244 1 : Map<String, dynamic>.from(
245 2 : Map<String, dynamic>.from(jsonDecode(e)))))
246 1 : .toList();
247 2 : _queueItemsStreamController.add(queueItems);
248 : }
249 :
250 1 : Future<void> _onPlayerPositionChanged(dynamic arguments) async {
251 : if (arguments == null) {
252 2 : _playerPositionStreamController.add(Duration.zero);
253 : return;
254 : }
255 1 : arguments = Map<String, dynamic>.from(arguments);
256 :
257 2 : final playerPosition = Duration(milliseconds: arguments["progress"] ?? 0);
258 2 : _playerPositionStreamController.add(playerPosition);
259 : }
260 :
261 1 : @override
262 : Future<void> queueReorderItems(
263 : {required List<int> itemsIds, required int? beforeItemWithId}) {
264 3 : return _channel.invokeMethod('queueReorderItems', {
265 : 'itemsIds': itemsIds,
266 : 'beforeItemWithId': beforeItemWithId,
267 : });
268 : }
269 : }
|