UNPKG

@codelab/nativescript-cast

Version:

NativeScript Chromecast Plugin.

551 lines 23 kB
import camelCase from 'lodash/fp/camelCase'; import snakeCase from 'lodash/fp/snakeCase'; import { Utils } from '@nativescript/core'; import { CastButtonBase } from './cast.common'; import { CastEvent, MetadataType, PlayerState, QueueType, RepeatMode, StreamType, } from './cast.types'; import { MediaQueueCallback } from './media-queue-callback.android'; import { MediaRouterCallback } from './media-router-callback.android'; import { RemoteMediaClientCallback } from './remote-media-client-callback.android'; import { SessionManagerListenerImpl } from './session-manager-listener.android'; const { MediaRouter, MediaRouteSelector, MediaControlIntent } = androidx.mediarouter.media; const { CastButtonFactory, CastContext, } = com.google.android.gms.cast.framework; const { MediaInfo, MediaLoadRequestData, MediaMetadata, MediaQueueData, MediaQueueItem, MediaStatus, MediaTrack, } = com.google.android.gms.cast; const METADATA_PREFIX = 'KEY_'; const ArrayList = java.util.ArrayList; const WebImage = com.google.android.gms.common.images.WebImage; let SessionManagerListener; function initSessionManagerListener() { if (SessionManagerListener) { return; } SessionManagerListener = SessionManagerListenerImpl; } let ProgressListener; function initProgressListener() { if (ProgressListener) { return; } } export function metadataTypeEnumToString(metadataType) { switch (metadataType) { case MediaMetadata.MEDIA_TYPE_MOVIE: return MetadataType.MOVIE; case MediaMetadata.MEDIA_TYPE_TV_SHOW: return MetadataType.TV_SHOW; case MediaMetadata.MEDIA_TYPE_MUSIC_TRACK: return MetadataType.MUSIC_TRACK; case MediaMetadata.MEDIA_TYPE_PHOTO: return MetadataType.PHOTO; case MediaMetadata.MEDIA_TYPE_USER: return MetadataType.USER; case MediaMetadata.MEDIA_TYPE_AUDIOBOOK_CHAPTER: return MetadataType.AUDIO_BOOK_CHAPTER; default: return MetadataType.GENERIC; } } export function metadataTypeStringToEnum(metadataType) { switch (metadataType) { case MetadataType.MOVIE: return MediaMetadata.MEDIA_TYPE_MOVIE; case MetadataType.TV_SHOW: return MediaMetadata.MEDIA_TYPE_TV_SHOW; case MetadataType.MUSIC_TRACK: return MediaMetadata.MEDIA_TYPE_MUSIC_TRACK; case MetadataType.PHOTO: return MediaMetadata.MEDIA_TYPE_PHOTO; case MetadataType.AUDIO_BOOK_CHAPTER: return MediaMetadata.MEDIA_TYPE_AUDIOBOOK_CHAPTER; case MetadataType.USER: return MediaMetadata.MEDIA_TYPE_USER; default: return MediaMetadata.MEDIA_TYPE_GENERIC; } } export function streamTypeEnumToString(streamType) { switch (streamType) { case MediaInfo.STREAM_TYPE_BUFFERED: return StreamType.BUFFERED; case MediaInfo.STREAM_TYPE_LIVE: return StreamType.LIVE; case MediaInfo.STREAM_TYPE_INVALID: return StreamType.UNKNOWN; default: return StreamType.NONE; } } export function streamTypeStringToEnum(streamType) { switch (streamType) { case StreamType.BUFFERED: return MediaInfo.STREAM_TYPE_BUFFERED; case StreamType.LIVE: return MediaInfo.STREAM_TYPE_LIVE; case StreamType.UNKNOWN: return MediaInfo.STREAM_TYPE_INVALID; default: return MediaInfo.STREAM_TYPE_NONE; } } export function repeatModeStringToEnum(repeatMode) { switch (repeatMode) { case RepeatMode.OFF: return MediaStatus.REPEAT_MODE_REPEAT_OFF; case RepeatMode.SINGLE: return MediaStatus.REPEAT_MODE_REPEAT_SINGLE; case RepeatMode.ALL: return MediaStatus.REPEAT_MODE_REPEAT_ALL; case RepeatMode.ALL_AND_SHUFFLE: return MediaStatus.REPEAT_MODE_REPEAT_ALL_AND_SHUFFLE; default: return MediaStatus.REPEAT_MODE_REPEAT_OFF; } } export function repeatModeEnumToString(repeatMode) { switch (repeatMode) { case MediaStatus.REPEAT_MODE_REPEAT_OFF: return RepeatMode.OFF; case MediaStatus.REPEAT_MODE_REPEAT_SINGLE: return RepeatMode.SINGLE; case MediaStatus.REPEAT_MODE_REPEAT_ALL: return RepeatMode.ALL; case MediaStatus.REPEAT_MODE_REPEAT_ALL_AND_SHUFFLE: return RepeatMode.ALL_AND_SHUFFLE; default: return RepeatMode.OFF; } } export function queueTypeStringToEnum(queueType) { switch (queueType) { case QueueType.ALBUM: return MediaQueueData.MEDIA_QUEUE_TYPE_ALBUM; case QueueType.PLAYLIST: return MediaQueueData.MEDIA_QUEUE_TYPE_PLAYLIST; case QueueType.AUDIO_BOOK: return MediaQueueData.MEDIA_QUEUE_TYPE_AUDIO_BOOK; case QueueType.RADIO_STATION: return MediaQueueData.MEDIA_QUEUE_TYPE_RADIO_STATION; case QueueType.PODCAST_SERIES: return MediaQueueData.MEDIA_QUEUE_TYPE_PODCAST_SERIES; case QueueType.TV_SERIES: return MediaQueueData.MEDIA_QUEUE_TYPE_TV_SERIES; case QueueType.VIDEO_PLAYLIST: return MediaQueueData.MEDIA_QUEUE_TYPE_VIDEO_PLAYLIST; case QueueType.LIVE_TV: return MediaQueueData.MEDIA_QUEUE_TYPE_LIVE_TV; case QueueType.MOVIE: return MediaQueueData.MEDIA_QUEUE_TYPE_MOVIE; default: return MediaQueueData.MEDIA_QUEUE_TYPE_GENERIC; } } export function queueTypeEnumToString(queueType) { switch (queueType) { case MediaQueueData.MEDIA_QUEUE_TYPE_ALBUM: return QueueType.ALBUM; case MediaQueueData.MEDIA_QUEUE_TYPE_PLAYLIST: return QueueType.PLAYLIST; case MediaQueueData.MEDIA_QUEUE_TYPE_AUDIO_BOOK: return QueueType.AUDIO_BOOK; case MediaQueueData.MEDIA_QUEUE_TYPE_RADIO_STATION: return QueueType.RADIO_STATION; case MediaQueueData.MEDIA_QUEUE_TYPE_PODCAST_SERIES: return QueueType.PODCAST_SERIES; case MediaQueueData.MEDIA_QUEUE_TYPE_TV_SERIES: return QueueType.TV_SERIES; case MediaQueueData.MEDIA_QUEUE_TYPE_VIDEO_PLAYLIST: return QueueType.VIDEO_PLAYLIST; case MediaQueueData.MEDIA_QUEUE_TYPE_LIVE_TV: return QueueType.LIVE_TV; case MediaQueueData.MEDIA_QUEUE_TYPE_MOVIE: return QueueType.MOVIE; default: return QueueType.GENERIC; } } export function convertMediaInfo(mediaInfo) { if (!mediaInfo) { return null; } const metadata = mediaInfo.getMetadata(); const metaDataKeys = Utils.ad.collections.stringSetToStringArray(metadata.keySet()); const images = metadata.getImages(); const tracks = mediaInfo.getMediaTracks(); const textTracks = []; if (tracks) { for (let i = 0; i < tracks.size(); i++) { const track = tracks.get(i); if (track.getType() === MediaTrack.TYPE_TEXT) { textTracks.push({ id: track.getId(), src: track.getContentId(), contentType: track.getContentType(), name: track.getName(), language: track.getLanguage(), }); } } } const castMetadata = { metadataType: metadataTypeEnumToString(metadata.getMediaType()), images: [], }; metaDataKeys.forEach(key => { const fixedKey = camelCase(key.replace('com.google.android.gms.cast.metadata.', '')); castMetadata[fixedKey] = metadata.getString(key); }); if (images) { for (let index = 0; index < images.size(); index++) { const img = images.get(index); castMetadata.images.push({ url: img.getUrl().toString(), width: img.getWidth(), height: img.getHeight() }); } } return { contentId: mediaInfo.getContentId(), streamType: streamTypeEnumToString(mediaInfo.getStreamType()), contentType: mediaInfo.getContentType(), metadata: castMetadata, duration: mediaInfo.getStreamDuration() / 1000, textTracks }; } export class CastButton extends CastButtonBase { constructor() { super(); } createNativeView() { const appContext = Utils.ad.getApplicationContext(); initSessionManagerListener(); const button = new androidx.mediarouter.app.MediaRouteButton(this._context); CastButtonFactory.setUpMediaRouteButton(appContext, button); this.mMediaRouter = MediaRouter.getInstance(appContext); this.mMediaRouteSelector = new MediaRouteSelector.Builder() .addControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO) .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) .build(); this.mMediaRouterCallback = new MediaRouterCallback(this); this.mCastContext = CastContext.getSharedInstance(appContext); this.mSessionManager = this.mCastContext.getSessionManager(); this.mSessionManagerListener = new SessionManagerListener(this); this.mRemoteMediaClientCallback = new RemoteMediaClientCallback(this); this.mMediaQeueuCallback = new MediaQueueCallback(this); this.addMediaRouterCallback(); this.addSessionManagerListener(); return button; } getNativeView() { return this.nativeView; } initNativeView() { this.nativeView.owner = this; super.initNativeView(); } disposeNativeView() { this.nativeView.owner = null; this.removeMediaRouterCallback(); this.removeSessionManagerListener(); super.disposeNativeView(); } addMediaRouterCallback() { this.mMediaRouter.addCallback(this.mMediaRouteSelector, this.mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); } removeMediaRouterCallback() { if (this.mMediaRouter && this.mMediaRouterCallback) { this.mMediaRouter.removeCallback(this.mMediaRouterCallback); } } addSessionManagerListener() { this.mSessionManager.addSessionManagerListener(this.mSessionManagerListener); } removeSessionManagerListener() { this.mSessionManager.removeSessionManagerListener(this.mSessionManagerListener); } addRemoteMediaClientCallback() { this.getRemoteMediaClient().registerCallback(this.mRemoteMediaClientCallback); } removeRemoteMediaClientCallback() { this.getRemoteMediaClient().unregisterCallback(this.mRemoteMediaClientCallback); } addMediaQueueCallback() { const mediaQueue = this.getRemoteMediaClient().getMediaQueue(); mediaQueue.registerCallback(this.mMediaQeueuCallback); } removeMediaQueueCallback() { const mediaQueue = this.getRemoteMediaClient().getMediaQueue(); mediaQueue.unregisterCallback(this.mMediaQeueuCallback); } addProgressListenenr() { this.getRemoteMediaClient().addProgressListener(this.mRemoteMediaClientProgressListener, 100); } removeProgressListener() { this.getRemoteMediaClient().removeProgressListener(this.mRemoteMediaClientProgressListener); } getRemoteMediaClient() { return this.mSessionManager.getCurrentCastSession().getRemoteMediaClient(); } buildMediaInfo(mediaInfo) { let metadata; if (mediaInfo.metadata) { const metadataType = metadataTypeStringToEnum(mediaInfo.metadata.metadataType); metadata = new MediaMetadata(metadataType); Object.keys(mediaInfo.metadata).forEach(key => { if (CastButtonBase.validMetadataKeys.indexOf(key) > -1) { const fixedKey = METADATA_PREFIX + snakeCase(key).toUpperCase(); const value = mediaInfo.metadata[key]; metadata.putString(MediaMetadata[fixedKey], value); } }); if (mediaInfo.metadata.images && mediaInfo.metadata.images.length) { mediaInfo.metadata.images.forEach(img => { const uri = android.net.Uri.parse(img.url); const thumb = new WebImage(uri, img.width, img.height); metadata.addImage(thumb); }); } } const streamType = streamTypeStringToEnum(mediaInfo.streamType); const mediaInformation = new MediaInfo.Builder(mediaInfo.contentId) .setContentType(mediaInfo.contentType) .setStreamType(MediaInfo[streamType]); if (metadata) { mediaInformation.setMetadata(metadata); } if (mediaInfo.duration) { mediaInformation.setStreamDuration(mediaInfo.duration); } if (mediaInfo.customData) { const customData = new org.json.JSONObject(JSON.stringify(mediaInfo.customData)); mediaInformation.setCustomData(customData); } if (mediaInfo.textTracks && mediaInfo.textTracks.length > 0) { const tracks = new ArrayList(); mediaInfo.textTracks.forEach((item, index) => { const track = new MediaTrack.Builder(index + 1, MediaTrack.TYPE_TEXT) .setContentId(item.src) .setContentType(item.contentType) .setSubtype(MediaTrack.SUBTYPE_SUBTITLES) .setName(item.name) .setLanguage(item.language) .build(); tracks.add(track); }); mediaInformation.setMediaTracks(tracks); } return mediaInformation.build(); } buildQueueItem(options) { const builtMediaInfo = this.buildMediaInfo(options.mediaInformation); const builder = new MediaQueueItem.Builder(builtMediaInfo) .setAutoplay(options.autoplay || true) .setCustomData(options.customData) .setPlaybackDuration(options.playbackDuration || Infinity) .setPreloadTime(options.preloadTime) .setStartTime(options.startTime || 0); if (options.activeTrackIds && options.activeTrackIds.length) { builder.setActiveTrackIds(options.activeTrackIds); } return builder.build(); } loadMedia(media, options) { const builtMediaInfo = this.buildMediaInfo(media); const requestData = new MediaLoadRequestData.Builder() .setAutoplay(new java.lang.Boolean(options.autoplay)) .setCredentials(options.credentials) .setCredentialsType(options.credentialsType) .setCurrentTime(options.startTime) .setCustomData(options.customData) .setMediaInfo(builtMediaInfo) .setPlaybackRate(options.playbackRate) .setQueueData(null); if (options.activeTrackIds && options.activeTrackIds.length) { requestData.setActiveTrackIds(options.activeTrackIds); } this.addRemoteMediaClientCallback(); this.addProgressListenenr(); this.getRemoteMediaClient().load(requestData.build()); } loadQueue(options) { const queueItems = new ArrayList(); options.items.forEach(item => { const queueItem = this.buildQueueItem(item); if (queueItem) { queueItems.add(queueItem); } else { console.log('queue item not built'); console.log(item); } }); const queueData = new MediaQueueData.Builder(); if (options) { queueData .setName(options.name) .setQueueId(options.queueID) .setQueueType(queueTypeStringToEnum(options.queueType)) .setRepeatMode(repeatModeStringToEnum(options.repeatMode)) .setStartIndex(options.startIndex || 0) .setStartTime(options.startTime || 0); } queueData.setItems(queueItems); this.addRemoteMediaClientCallback(); this.addMediaQueueCallback(); const requestData = new MediaLoadRequestData.Builder() .setAutoplay(new java.lang.Boolean(options.autoplay || true)) .setQueueData(queueData.build()); this.getRemoteMediaClient().load(requestData.build()); } showController() { const intent = new android.content.Intent(this._context, com.google.android.gms.cast.framework.media.widget.ExpandedControllerActivity.class); this._context.startActivity(intent); } showCastInstructions(title = 'Touch to cast media to your TV', singleTime = true) { const overlay = new com.google.android.gms.cast.framework.IntroductoryOverlay.Builder(this._context, this.getNativeView()) .setTitleText(title); if (singleTime) { overlay.setSingleTime(); } overlay.build().show(); } showCastDialog() { this.getNativeView().showDialog(); } getMediaInfo() { const mediaInfo = this.getRemoteMediaClient().getMediaInfo(); return convertMediaInfo(mediaInfo); } pauseMedia(customData) { this.getRemoteMediaClient().pause(customData); } playMedia(customData) { this.getRemoteMediaClient().play(customData); } seekMedia(position, resumeState = 0, customData) { this.getRemoteMediaClient().seek(position * 1000, resumeState, customData); } stopMedia(customData) { this.getRemoteMediaClient().stop(customData); } setActiveTrackIds(trackIds) { this.getRemoteMediaClient().setActiveMediaTracks(trackIds); } setTintColor() { console.log('setTintColor not available for Android.'); } setVolume(volume, customData) { const remoteMediaClient = this.getRemoteMediaClient(); remoteMediaClient.setStreamVolume(volume, customData); } setMuted(muted, customData) { const remoteMediaClient = this.getRemoteMediaClient(); remoteMediaClient.setStreamMute(muted, customData); } queueNextItem() { this.getRemoteMediaClient().queueNext(null); } queuePreviousItem() { this.getRemoteMediaClient().queuePrev(null); } queueSetRepeatMode(repeatMode) { this.getRemoteMediaClient().queueSetRepeatMode(repeatModeStringToEnum(repeatMode), null); } queueFetchItemIDs() { console.log('queueFetchItemIDs not implemented'); } queueFetchItemAtIndex(index) { console.log('queueFetchItemAtIndex not implemented'); } queueInsertItem(options) { const queueItem = this.buildQueueItem(options.item); if (options.play) { this.getRemoteMediaClient().queueInsertAndPlayItem(queueItem, options.beforeItemID || com.google.android.gms.cast.MediaQueueItem.INVALID_ITEM_ID, options.playPosition || 0, options.customData); } else { this.getRemoteMediaClient().queueInsertItems([queueItem], options.beforeItemID || com.google.android.gms.cast.MediaQueueItem.INVALID_ITEM_ID, options.customData); } } queueInsertItems(options) { const queueItems = []; options.items.forEach(item => { const queueItem = this.buildQueueItem(item); queueItems.push(queueItem); }); this.getRemoteMediaClient().queueInsertItems(queueItems, options.beforeItemID || com.google.android.gms.cast.MediaQueueItem.INVALID_ITEM_ID, options.customData); } queueRemoveItems(itemIDs, customData) { this.getRemoteMediaClient().queueRemoveItems(itemIDs, customData); } queueReorderItems(itemIDs, beforeItemID, customData) { this.getRemoteMediaClient().queueReorderItems(itemIDs, beforeItemID || com.google.android.gms.cast.MediaQueueItem.INVALID_ITEM_ID, customData); } queueJumpToItem(itemID, playPosition, customData) { this.getRemoteMediaClient().queueJumpToItem(itemID, playPosition, customData); } queueUpdateItems(options) { console.log('queueUpdateItems not implemented'); } onMediaStatusUpdate() { const mediaStatus = this.getRemoteMediaClient().getMediaStatus(); let info = null; let status = null; if (mediaStatus) { const mediaInfo = mediaStatus.getMediaInfo(); if (mediaInfo) { info = convertMediaInfo(mediaInfo); } let playerState = PlayerState.UNKNOWN; const trackIds = mediaStatus.getActiveTrackIds(); const activeTrackIds = []; if (trackIds) { for (let i = 0; i < trackIds.length; i++) { activeTrackIds.push(trackIds[i]); } } switch (mediaStatus.getPlayerState()) { case MediaStatus.PLAYER_STATE_IDLE: playerState = PlayerState.IDLE; break; case MediaStatus.PLAYER_STATE_PLAYING: playerState = PlayerState.PLAYING; break; case MediaStatus.PLAYER_STATE_PAUSED: playerState = PlayerState.PAUSED; break; case MediaStatus.PLAYER_STATE_BUFFERING: playerState = PlayerState.BUFFERING; break; } const queueData = mediaStatus.getQueueData(); status = { activeTrackIds, playerState, idleReason: mediaStatus.getIdleReason(), isMuted: mediaStatus.isMute(), playbackRate: mediaStatus.getPlaybackRate(), streamPosition: mediaStatus.getStreamPosition(), volume: mediaStatus.getStreamVolume(), currentItemID: mediaStatus.getCurrentItemId(), loadingItemID: mediaStatus.getLoadingItemId(), preloadedItemID: mediaStatus.getPreloadedItemId(), queueData: { name: queueData.getName(), queueID: queueData.getQueueId(), queueType: queueTypeEnumToString(queueData.getQueueType()), repeatMode: repeatModeEnumToString(queueData.getRepeatMode()), startIndex: queueData.getStartIndex(), startTime: queueData.getStartTime(), }, queueItemCount: mediaStatus.getQueueItemCount(), }; } this.sendEvent(CastButtonBase.castEvent, { eventName: CastEvent.onMediaStatusChanged, status, info, android: this.nativeView }); } } //# sourceMappingURL=cast.android.js.map