Pass Receiver objects instead of ReceiverDevice objects to cast API

This commit is contained in:
hensm
2022-08-29 09:43:10 +01:00
committed by Matt Hensman
parent 48d5e7e0fe
commit 83c81219d7
8 changed files with 185 additions and 234 deletions

View File

@@ -73,7 +73,7 @@ export default class Session extends CastClient {
this.onSessionCreated?.(this.sessionId); this.onSessionCreated?.(this.sessionId);
messaging.sendMessage({ messaging.sendMessage({
subject: "cast:sessionCreated", subject: "main:castSessionCreated",
data: { data: {
sessionId: this.sessionId, sessionId: this.sessionId,
statusText: application.statusText, statusText: application.statusText,
@@ -103,7 +103,7 @@ export default class Session extends CastClient {
} }
messaging.sendMessage({ messaging.sendMessage({
subject: "cast:sessionUpdated", subject: "main:castSessionUpdated",
data: { data: {
sessionId: this.sessionId, sessionId: this.sessionId,
statusText: application.statusText, statusText: application.statusText,

View File

@@ -110,8 +110,8 @@ type MessageDefinitions = {
* updates. Updated details is a mutable subset of session details * updates. Updated details is a mutable subset of session details
* otherwise fixed on creation. * otherwise fixed on creation.
*/ */
"cast:sessionCreated": CastSessionCreatedDetails; "main:castSessionCreated": CastSessionCreatedDetails;
"cast:sessionUpdated": CastSessionUpdatedDetails; "main:castSessionUpdated": CastSessionUpdatedDetails;
/** /**
* Sent to cast API instances whenever a session is stopped. * Sent to cast API instances whenever a session is stopped.
*/ */

View File

@@ -16,8 +16,10 @@ import deviceManager from "./deviceManager";
import castManager from "./castManager"; import castManager from "./castManager";
import { BaseConfig, baseConfigStorage, getAppTag } from "../cast/googleApi"; import { BaseConfig, baseConfigStorage, getAppTag } from "../cast/googleApi";
import type { SessionRequest } from "../cast/sdk/classes";
import type { SenderMediaMessage, SenderMessage } from "../cast/sdk/types"; import type { SenderMediaMessage, SenderMessage } from "../cast/sdk/types";
import type { SessionRequest } from "../cast/sdk/classes";
import { ReceiverAction } from "../cast/sdk/enums";
import { createReceiver } from "../cast/utils";
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html"); const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
@@ -330,7 +332,10 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
* If the current context is running the mirroring app, pretend * If the current context is running the mirroring app, pretend
* it doesn't exist because it shouldn't be launched like this. * it doesn't exist because it shouldn't be launched like this.
*/ */
if (castInstance?.appId === (await options.get("mirroringAppId"))) { if (
castInstance?.apiConfig?.sessionRequest.appId ===
(await options.get("mirroringAppId"))
) {
castInstance = undefined; castInstance = undefined;
} }
@@ -377,7 +382,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
await deviceManager.init(); await deviceManager.init();
let isRequestAppAudioCompatible: Optional<boolean>; let isRequestAppAudioCompatible: Optional<boolean>;
if (castInstance?.appId) { if (castInstance?.apiConfig?.sessionRequest.appId) {
if (!baseConfig) { if (!baseConfig) {
try { try {
baseConfig = (await baseConfigStorage.get("baseConfig")) baseConfig = (await baseConfigStorage.get("baseConfig"))
@@ -389,7 +394,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
isRequestAppAudioCompatible = getAppTag( isRequestAppAudioCompatible = getAppTag(
baseConfig, baseConfig,
castInstance.appId castInstance.apiConfig?.sessionRequest.appId
)?.supports_audio_only; )?.supports_audio_only;
} }
@@ -429,7 +434,7 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
receiverDevices: deviceManager.getDevices(), receiverDevices: deviceManager.getDevices(),
defaultMediaType, defaultMediaType,
availableMediaTypes, availableMediaTypes,
appId: castInstance?.appId, appId: castInstance?.apiConfig?.sessionRequest.appId,
// Create page info // Create page info
pageInfo: pageUrl pageInfo: pageUrl
? { ? {
@@ -464,9 +469,15 @@ function createSelector() {
); );
if (!castInstance) return; if (!castInstance) return;
const device = deviceManager.getDeviceById(ev.detail.deviceId);
if (!device) return;
castInstance.contentPort.postMessage({ castInstance.contentPort.postMessage({
subject: "cast:receiverStoppedAction", subject: "cast:sendReceiverAction",
data: { deviceId: ev.detail.deviceId } data: {
receiver: createReceiver(device),
action: ReceiverAction.STOP
}
}); });
}; };
selector.addEventListener("stop", onStop); selector.addEventListener("stop", onStop);

View File

@@ -8,6 +8,10 @@ import { stringify } from "../lib/utils";
import { ReceiverSelectorMediaType } from "../types"; import { ReceiverSelectorMediaType } from "../types";
import type { ApiConfig } from "../cast/sdk/classes";
import { ReceiverAction } from "../cast/sdk/enums";
import { createReceiver } from "../cast/utils";
import deviceManager from "./deviceManager"; import deviceManager from "./deviceManager";
import ReceiverSelector, { ReceiverSelection } from "./ReceiverSelector"; import ReceiverSelector, { ReceiverSelection } from "./ReceiverSelector";
@@ -18,7 +22,10 @@ export interface CastInstance {
contentPort: AnyPort; contentPort: AnyPort;
contentTabId?: number; contentTabId?: number;
contentFrameId?: number; contentFrameId?: number;
appId?: string;
/** ApiConfig provided on initialization. */
apiConfig?: ApiConfig;
/** Established session details. */
session?: CastSession; session?: CastSession;
} }
@@ -39,23 +46,23 @@ export default new (class {
} }
}); });
// Forward receiver eventes to cast instances // Pass receiver availability updates to cast API.
deviceManager.addEventListener("deviceUp", ev => { const updateReceiverAvailability = () => {
const isAvailable = deviceManager.getDevices().length > 0;
for (const instance of this.activeInstances) { for (const instance of this.activeInstances) {
instance.contentPort.postMessage({ instance.contentPort.postMessage({
subject: "cast:receiverDeviceUp", subject: "cast:updateReceiverAvailability",
data: { receiverDevice: ev.detail.deviceInfo } data: { isAvailable }
}); });
} }
}); };
deviceManager.addEventListener("deviceDown", ev => {
for (const instance of this.activeInstances) { deviceManager.addEventListener("deviceUp", updateReceiverAvailability);
instance.contentPort.postMessage({ deviceManager.addEventListener(
subject: "cast:receiverDeviceDown", "deviceDown",
data: { receiverDeviceId: ev.detail.deviceId } updateReceiverAvailability
}); );
}
});
} }
/** /**
@@ -197,7 +204,7 @@ export default new (class {
) { ) {
// Intercept messages to store relevant info // Intercept messages to store relevant info
switch (message.subject) { switch (message.subject) {
case "cast:sessionCreated": { case "main:castSessionCreated": {
// Close after session is created // Close after session is created
const selector = ReceiverSelector.sharedInstance; const selector = ReceiverSelector.sharedInstance;
if ( if (
@@ -211,12 +218,38 @@ export default new (class {
selector.close(); selector.close();
} }
const { receiverId: deviceId } = message.data;
instance.session = { instance.session = {
deviceId: message.data.receiverId, deviceId,
sessionId: message.data.sessionId sessionId: message.data.sessionId
}; };
const device = deviceManager.getDeviceById(deviceId);
if (!device) {
logger.error(
"[on main:castSessionCreated]: Could not find device with ID:",
deviceId
);
break;
}
instance.contentPort.postMessage({
subject: "cast:sessionCreated",
data: {
...message.data,
receiver: createReceiver(device)
}
});
break; break;
} }
case "main:castSessionUpdated":
instance.contentPort.postMessage({
subject: "cast:sessionUpdated",
data: message.data
});
} }
instance.contentPort.postMessage(message); instance.contentPort.postMessage(message);
@@ -239,14 +272,14 @@ export default new (class {
switch (message.subject) { switch (message.subject) {
// Cast API has been initialized // Cast API has been initialized
case "main:initializeCast": { case "main:initializeCast": {
instance.appId = message.data.appId; instance.apiConfig = message.data.apiConfig;
for (const receiverDevice of deviceManager.getDevices()) { instance.contentPort.postMessage({
instance.contentPort.postMessage({ subject: "cast:updateReceiverAvailability",
subject: "cast:receiverDeviceUp", data: {
data: { receiverDevice } isAvailable: deviceManager.getDevices().length > 0
}); }
} });
break; break;
} }
@@ -262,11 +295,13 @@ export default new (class {
); );
} }
const { sessionRequest } = message.data;
try { try {
const selection = await ReceiverSelector.getSelection( const selection = await ReceiverSelector.getSelection(
instance.contentTabId, instance.contentTabId,
instance.contentFrameId, instance.contentFrameId,
{ sessionRequest: message.data.sessionRequest } { sessionRequest }
); );
// Handle cancellation // Handle cancellation
@@ -297,9 +332,12 @@ export default new (class {
break; break;
} }
instance.contentPort.postMessage({ instance.bridgePort.postMessage({
subject: "cast:selectReceiver/selected", subject: "bridge:createCastSession",
data: selection data: {
appId: sessionRequest.appId,
receiverDevice: selection.receiverDevice
}
}); });
} catch (err) { } catch (err) {
// TODO: Report errors properly // TODO: Report errors properly
@@ -336,9 +374,24 @@ export default new (class {
); );
} }
if (!instance.apiConfig?.sessionRequest.appId) {
throw logger.error("Invalid session request");
}
instance.contentPort.postMessage({ instance.contentPort.postMessage({
subject: "cast:launchApp", subject: "cast:sendReceiverAction",
data: { receiverDevice: opts.selection.receiverDevice } data: {
receiver: createReceiver(opts.selection.receiverDevice),
action: ReceiverAction.CAST
}
});
instance.bridgePort.postMessage({
subject: "bridge:createCastSession",
data: {
appId: instance.apiConfig?.sessionRequest.appId,
receiverDevice: opts.selection.receiverDevice
}
}); });
break; break;

View File

@@ -30,8 +30,8 @@ interface EventMap {
export default new (class extends TypedEventTarget<EventMap> { export default new (class extends TypedEventTarget<EventMap> {
/** /**
* Map of receiver device IDs to devices. Updated as * Map of receiver device IDs to devices. Updated as receiverDevice
* receiverDevice messages are received from the bridge. * messages are received from the bridge.
*/ */
private receiverDevices = new Map<string, ReceiverDevice>(); private receiverDevices = new Map<string, ReceiverDevice>();
@@ -43,8 +43,8 @@ export default new (class extends TypedEventTarget<EventMap> {
} }
/** /**
* Initialize (or re-initialize) a bridge connection to * Initializes (or re-initializes) a bridge connection to start
* start dispatching events. * dispatching events.
*/ */
async refresh() { async refresh() {
this.bridgePort?.disconnect(); this.bridgePort?.disconnect();
@@ -63,33 +63,16 @@ export default new (class extends TypedEventTarget<EventMap> {
}); });
} }
/** /** Gets a list of receiver devices. */
* Get a list of receiver devices
*/
getDevices() { getDevices() {
return Array.from(this.receiverDevices.values()); return Array.from(this.receiverDevices.values());
} }
/** Gets a device by ID. */
/** getDeviceById(deviceId: string) {
* Stops a receiver app running on a given device. return this.receiverDevices.get(deviceId);
*/
stopReceiverApp(receiverDeviceId: string) {
if (!this.bridgePort) {
logger.error(
"Failed to stop receiver device, no bridge connection"
);
return;
}
const receiverDevice = this.receiverDevices.get(receiverDeviceId);
if (receiverDevice) {
this.bridgePort.postMessage({
subject: "bridge:stopCastSession",
data: { receiverDevice }
});
}
} }
/** Sends an NS_RECEIVER message to a given device. */
sendReceiverMessage(deviceId: string, message: SenderMessage) { sendReceiverMessage(deviceId: string, message: SenderMessage) {
if (!this.bridgePort) { if (!this.bridgePort) {
logger.error( logger.error(
@@ -112,6 +95,7 @@ export default new (class extends TypedEventTarget<EventMap> {
}); });
} }
/** Sends an NS_MEDIA message to a given device. */
sendMediaMessage(deviceId: string, message: SenderMediaMessage) { sendMediaMessage(deviceId: string, message: SenderMediaMessage) {
if (!this.bridgePort) { if (!this.bridgePort) {
logger.error("Failed to send media message (no bridge connection)"); logger.error("Failed to send media message (no bridge connection)");

View File

@@ -5,7 +5,6 @@ import logger from "../../lib/logger";
import type { Message } from "../../messaging"; import type { Message } from "../../messaging";
import eventMessaging from "../eventMessaging"; import eventMessaging from "../eventMessaging";
import type { ReceiverDevice } from "../../types";
import type { ErrorCallback, SuccessCallback } from "../types"; import type { ErrorCallback, SuccessCallback } from "../types";
import { import {
@@ -39,7 +38,6 @@ import {
import Session from "./Session"; import Session from "./Session";
import media from "./media"; import media from "./media";
import { convertCapabilitiesFlags } from "../utils";
type ReceiverActionListener = ( type ReceiverActionListener = (
receiver: Receiver, receiver: Receiver,
@@ -48,35 +46,21 @@ type ReceiverActionListener = (
type RequestSessionSuccessCallback = (session: Session) => void; type RequestSessionSuccessCallback = (session: Session) => void;
/**
* Create `chrome.cast.Receiver` object from receiver device info.
*/
function createReceiver(device: ReceiverDevice) {
const receiver = new Receiver(
device.id,
device.friendlyName,
convertCapabilitiesFlags(device.capabilities)
);
// Currently only supports CAST receivers
receiver.receiverType = ReceiverType.CAST;
return receiver;
}
/** Cast SDK root class */ /** Cast SDK root class */
export default class { export default class {
#receiverDevices = new Map<string, ReceiverDevice>();
#apiConfig?: ApiConfig; #apiConfig?: ApiConfig;
#sessionRequest?: SessionRequest; #sessionRequest?: SessionRequest;
#requestSessionSuccessCallback?: RequestSessionSuccessCallback; #requestSessionSuccessCallback?: RequestSessionSuccessCallback;
#requestSessionErrorCallback?: ErrorCallback; #requestSessionErrorCallback?: ErrorCallback;
#initializeSuccessCallback?: SuccessCallback;
#sessions = new Map<string, Session>(); #sessions = new Map<string, Session>();
#receiverActionListeners = new Set<ReceiverActionListener>(); #receiverActionListeners = new Set<ReceiverActionListener>();
#receiverAvailability = ReceiverAvailability.UNAVAILABLE;
// Enums // Enums
AutoJoinPolicy = AutoJoinPolicy; AutoJoinPolicy = AutoJoinPolicy;
Capability = Capability; Capability = Capability;
@@ -114,29 +98,15 @@ export default class {
eventMessaging.page.addListener(this.#onMessage.bind(this)); eventMessaging.page.addListener(this.#onMessage.bind(this));
} }
#sendSessionRequest(
sessionRequest: SessionRequest,
receiverDevice: ReceiverDevice
) {
for (const listener of this.#receiverActionListeners) {
listener(createReceiver(receiverDevice), ReceiverAction.CAST);
}
eventMessaging.page.sendMessage({
subject: "bridge:createCastSession",
data: {
appId: sessionRequest.appId,
receiverDevice: receiverDevice
}
});
}
#onMessage(message: Message) { #onMessage(message: Message) {
switch (message.subject) { switch (message.subject) {
case "cast:initialized": { case "cast:initialized":
this.#initializeSuccessCallback?.();
this.#apiConfig?.receiverListener(this.#receiverAvailability);
this.isAvailable = true; this.isAvailable = true;
break; break;
}
/** /**
* Once the bridge detects a session creation, session info * Once the bridge detects a session creation, session info
@@ -144,19 +114,9 @@ export default class {
*/ */
case "cast:sessionCreated": { case "cast:sessionCreated": {
const status = message.data; const status = message.data;
const receiverDevice = this.#receiverDevices.get(
status.receiverId
);
if (!receiverDevice) {
logger.error(
`Could not find receiver device "${status.receiverFriendlyName}" (${status.receiverId})`
);
break;
}
const receiver = createReceiver(receiverDevice); status.receiver.volume = status.volume;
receiver.volume = status.volume; status.receiver.displayStatus = new ReceiverDisplayStatus(
receiver.displayStatus = new ReceiverDisplayStatus(
status.statusText, status.statusText,
status.appImages status.appImages
); );
@@ -166,7 +126,7 @@ export default class {
status.appId, status.appId,
status.displayName, status.displayName,
status.appImages, status.appImages,
receiver status.receiver
); );
session.namespaces = status.namespaces; session.namespaces = status.namespaces;
@@ -178,9 +138,9 @@ export default class {
/** /**
* If session created via requestSession, the success * If session created via requestSession, the success
* callback will be set, otherwise the session was created * callback will be set, otherwise the session was
* by the extension and the session listener should be * created by the extension and the session listener
* called instead. * should be called instead.
*/ */
if (this.#requestSessionSuccessCallback) { if (this.#requestSessionSuccessCallback) {
this.#requestSessionSuccessCallback(session); this.#requestSessionSuccessCallback(session);
@@ -263,49 +223,15 @@ export default class {
break; break;
} }
case "cast:receiverDeviceUp": { case "cast:updateReceiverAvailability": {
const { receiverDevice } = message.data; const availability = message.data.isAvailable
if (this.#receiverDevices.has(receiverDevice.id)) { ? ReceiverAvailability.AVAILABLE
break; : ReceiverAvailability.UNAVAILABLE;
}
this.#receiverDevices.set(receiverDevice.id, receiverDevice); // If availability has changed, call receiver listeners
if (availability !== this.#receiverAvailability) {
if (this.#apiConfig) { this.#receiverAvailability = availability;
// Notify listeners of new cast destination this.#apiConfig?.receiverListener(availability);
this.#apiConfig.receiverListener(
ReceiverAvailability.AVAILABLE
);
}
break;
}
case "cast:receiverDeviceDown": {
const { receiverDeviceId } = message.data;
this.#receiverDevices.delete(receiverDeviceId);
if (this.#receiverDevices.size === 0) {
if (this.#apiConfig) {
this.#apiConfig.receiverListener(
ReceiverAvailability.UNAVAILABLE
);
}
}
break;
}
case "cast:selectReceiver/selected": {
logger.info("Selected receiver");
if (this.#sessionRequest) {
this.#sendSessionRequest(
this.#sessionRequest,
message.data.receiverDevice
);
this.#sessionRequest = undefined;
} }
break; break;
@@ -324,35 +250,13 @@ export default class {
break; break;
} }
case "cast:receiverStoppedAction": { case "cast:sendReceiverAction": {
const device = this.#receiverDevices.get(message.data.deviceId);
if (!device) break;
for (const actionListener of this.#receiverActionListeners) { for (const actionListener of this.#receiverActionListeners) {
actionListener(createReceiver(device), ReceiverAction.STOP); actionListener(message.data.receiver, message.data.action);
} }
break; break;
} }
// Session request initiated via receiver selector
case "cast:launchApp": {
if (this.#sessionRequest) {
logger.error("Session request already in progress.");
break;
}
if (!this.#apiConfig?.sessionRequest) {
logger.error("Session request not found!");
break;
}
this.#sendSessionRequest(
this.#apiConfig.sessionRequest,
message.data.receiverDevice
);
break;
}
} }
} }
@@ -371,25 +275,20 @@ export default class {
this.#apiConfig = apiConfig; this.#apiConfig = apiConfig;
if (successCallback) {
this.#initializeSuccessCallback = successCallback;
}
eventMessaging.page.sendMessage({ eventMessaging.page.sendMessage({
subject: "main:initializeCast", subject: "main:initializeCast",
data: { appId: this.#apiConfig.sessionRequest.appId } data: { apiConfig: this.#apiConfig }
}); });
successCallback?.();
this.#apiConfig.receiverListener(
this.#receiverDevices.size
? ReceiverAvailability.AVAILABLE
: ReceiverAvailability.UNAVAILABLE
);
} }
requestSession( requestSession(
successCallback: RequestSessionSuccessCallback, successCallback: RequestSessionSuccessCallback,
errorCallback: ErrorCallback, errorCallback: ErrorCallback,
newSessionRequest?: SessionRequest, newSessionRequest?: SessionRequest
receiverDevice?: ReceiverDevice
) { ) {
logger.info("cast.requestSession"); logger.info("cast.requestSession");
@@ -410,40 +309,23 @@ export default class {
return; return;
} }
// No receivers available if (this.#receiverAvailability === ReceiverAvailability.UNAVAILABLE) {
if (!this.#receiverDevices.size) {
errorCallback?.(new Error_(ErrorCode.RECEIVER_UNAVAILABLE)); errorCallback?.(new Error_(ErrorCode.RECEIVER_UNAVAILABLE));
return; return;
} }
/** // Store used session request
* Store session request for use in return message from
* receiver selection.
*/
this.#sessionRequest = this.#sessionRequest =
newSessionRequest ?? this.#apiConfig.sessionRequest; newSessionRequest ?? this.#apiConfig.sessionRequest;
this.#requestSessionSuccessCallback = successCallback; this.#requestSessionSuccessCallback = successCallback;
this.#requestSessionErrorCallback = errorCallback; this.#requestSessionErrorCallback = errorCallback;
/** // Open receiver selector UI
* If a receiver was provided, skip the receiver selector eventMessaging.page.sendMessage({
* process. subject: "main:selectReceiver",
*/ data: { sessionRequest: this.#sessionRequest }
if (receiverDevice) { });
if (
receiverDevice?.id &&
this.#receiverDevices.has(receiverDevice.id)
) {
this.#sendSessionRequest(this.#sessionRequest, receiverDevice);
}
} else {
// Open receiver selector UI
eventMessaging.page.sendMessage({
subject: "main:selectReceiver",
data: { sessionRequest: this.#sessionRequest }
});
}
} }
requestSessionById(_sessionId: string): void { requestSessionById(_sessionId: string): void {

View File

@@ -1,5 +1,6 @@
import { ReceiverDevice, ReceiverDeviceCapabilities } from "../types"; import { ReceiverDevice, ReceiverDeviceCapabilities } from "../types";
import { Capability } from "./sdk/enums"; import { Receiver } from "./sdk/classes";
import { Capability, ReceiverType } from "./sdk/enums";
/** /**
* Check receiver device capabilities bitflags against array of * Check receiver device capabilities bitflags against array of
@@ -64,3 +65,19 @@ export function getEstimatedTime(opts: GetEstimatedTimeOpts) {
return estimatedTime; return estimatedTime;
} }
/**
* Create `chrome.cast.Receiver` object from receiver device info.
*/
export function createReceiver(device: ReceiverDevice) {
const receiver = new Receiver(
device.id,
device.friendlyName,
convertCapabilitiesFlags(device.capabilities)
);
// Currently only supports CAST receivers
receiver.receiverType = ReceiverType.CAST;
return receiver;
}

View File

@@ -16,9 +16,10 @@ import type {
SenderMediaMessage, SenderMediaMessage,
SenderMessage SenderMessage
} from "./cast/sdk/types"; } from "./cast/sdk/types";
import type { SessionRequest } from "./cast/sdk/classes"; import type { ApiConfig, Receiver, SessionRequest } from "./cast/sdk/classes";
import type { ReceiverDevice, ReceiverSelectorMediaType } from "./types"; import type { ReceiverDevice, ReceiverSelectorMediaType } from "./types";
import type { ReceiverAction } from "./cast/sdk/enums";
/** /**
* Messages are JSON objects with a `subject` string key and a * Messages are JSON objects with a `subject` string key and a
@@ -76,24 +77,26 @@ type ExtMessageDefinitions = {
"main:selectReceiver": { "main:selectReceiver": {
sessionRequest: SessionRequest; sessionRequest: SessionRequest;
}; };
/** Return message to the cast API when a receiver is selected. */
"cast:selectReceiver/selected": ReceiverSelection;
/** Return message to the cast API when a selection is cancelled. */ /** Return message to the cast API when a selection is cancelled. */
"cast:selectReceiver/cancelled": undefined; "cast:selectReceiver/cancelled": undefined;
/** Sent to the cast API when a receiver app is stopped. */ /**
"cast:receiverStoppedAction": { deviceId: string }; * Sent to the cast API when a session is requested or stopped via
* the extension UI.
*/
"cast:sendReceiverAction": { receiver: Receiver; action: ReceiverAction };
/** /**
* Tells the cast manager to provide the cast API instance with * Tells the cast manager to provide the cast API instance with
* receiver data. * receiver data.
*/ */
"main:initializeCast": { appId: string }; "main:initializeCast": { apiConfig: ApiConfig };
"cast:initialized": { isAvailable: boolean }; "cast:initialized": { isAvailable: boolean };
"cast:receiverDeviceUp": { receiverDevice: ReceiverDevice }; "cast:sessionCreated": CastSessionCreatedDetails & { receiver: Receiver };
"cast:receiverDeviceDown": { receiverDeviceId: string }; "cast:sessionUpdated": CastSessionUpdatedDetails;
"cast:launchApp": { receiverDevice: ReceiverDevice };
"cast:updateReceiverAvailability": { isAvailable: boolean };
}; };
/** /**
@@ -189,8 +192,9 @@ type AppMessageDefinitions = {
* updates. Updated details is a mutable subset of session details * updates. Updated details is a mutable subset of session details
* otherwise fixed on creation. * otherwise fixed on creation.
*/ */
"cast:sessionCreated": CastSessionCreatedDetails; "main:castSessionCreated": CastSessionCreatedDetails;
"cast:sessionUpdated": CastSessionUpdatedDetails; "main:castSessionUpdated": CastSessionUpdatedDetails;
/** /**
* Sent to cast API instances whenever a session is stopped. * Sent to cast API instances whenever a session is stopped.
*/ */