Move selectorManager getSelection to ReceiverSelector static method

This commit is contained in:
hensm
2022-08-25 22:34:54 +01:00
parent deb4c5154d
commit 7a60bb3278
5 changed files with 275 additions and 272 deletions

View File

@@ -3,9 +3,9 @@
import logger from "../lib/logger";
import messaging, { Port, Message } from "../messaging";
import options from "../lib/options";
import { TypedEventTarget } from "../lib/TypedEventTarget";
import { SenderMediaMessage, SenderMessage } from "../cast/sdk/types";
import { getMediaTypesForPageUrl } from "../lib/utils";
import {
ReceiverDevice,
ReceiverSelectionActionType,
@@ -13,6 +13,13 @@ import {
ReceiverSelectorPageInfo
} from "../types";
import deviceManager from "./deviceManager";
import castManager from "./castManager";
import { BaseConfig, baseConfigStorage, getAppTag } from "../cast/googleApi";
import type { SessionRequest } from "../cast/sdk/classes";
import type { SenderMediaMessage, SenderMessage } from "../cast/sdk/types";
const POPUP_URL = browser.runtime.getURL("ui/popup/index.html");
export interface ReceiverSelectionCast {
@@ -45,6 +52,16 @@ interface ReceiverSelectorEvents {
mediaMessage: ReceiverSelectorMediaMessage;
}
let baseConfig: BaseConfig;
baseConfigStorage
.get("baseConfig")
.then(value => {
baseConfig = value.baseConfig;
})
.catch(() => {
logger.error("Failed to get Chromecast base config!");
});
/**
* Manages the receiver selector popup window and communication with the
* extension page hosted within.
@@ -316,4 +333,249 @@ export default class ReceiverSelector extends TypedEventTarget<ReceiverSelectorE
}
}
}
static shared = new ReceiverSelector();
/**
* Opens a receiver selector with the specified default/available media
* types.
*
* Returns a promise that:
* - Resolves to a ReceiverSelection object if selection is
* successful.
* - Resolves to null if the selection is cancelled.
* - Rejects if the selection fails.
*/
static getSelection(
contextTabId: number,
contextFrameId = 0,
selectionOpts?: {
sessionRequest?: SessionRequest;
withMediaSender?: boolean;
}
): Promise<ReceiverSelection | null> {
return new Promise(async (resolve, reject) => {
let castInstance = castManager.getInstance(
contextTabId,
contextFrameId
);
/**
* If the current context is running the mirroring app, pretend
* it doesn't exist because it shouldn't be launched like this.
*/
if (castInstance?.appId === (await options.get("mirroringAppId"))) {
castInstance = undefined;
}
let defaultMediaType = ReceiverSelectorMediaType.Tab;
let availableMediaTypes = ReceiverSelectorMediaType.None;
let pageUrl: string | undefined;
try {
pageUrl = (
await browser.webNavigation.getFrame({
tabId: contextTabId,
frameId: contextFrameId
})
).url;
availableMediaTypes = getMediaTypesForPageUrl(pageUrl);
} catch {
logger.error(
"Failed to locate frame, falling back to default available media types."
);
}
// Enable app media type if sender application is present
if (castInstance || selectionOpts?.withMediaSender) {
defaultMediaType = ReceiverSelectorMediaType.App;
availableMediaTypes |= ReceiverSelectorMediaType.App;
}
const opts = await options.getAll();
// Disable mirroring media types if mirroring is not enabled
if (!opts.mirroringEnabled) {
availableMediaTypes &= ~(
ReceiverSelectorMediaType.Tab |
ReceiverSelectorMediaType.Screen
);
}
// Remove file media type if local media is not enabled
if (!opts.mediaEnabled || !opts.localMediaEnabled) {
availableMediaTypes &= ~ReceiverSelectorMediaType.File;
}
// Close an existing open selector
if (ReceiverSelector.shared && ReceiverSelector.shared.isOpen) {
ReceiverSelector.shared.close();
}
// Get a new selector for each selection
ReceiverSelector.shared = new ReceiverSelector();
function onReceiverChange() {
ReceiverSelector.shared.update(deviceManager.getDevices());
}
deviceManager.addEventListener(
"receiverDeviceUp",
onReceiverChange
);
deviceManager.addEventListener(
"receiverDeviceDown",
onReceiverChange
);
deviceManager.addEventListener(
"receiverDeviceUpdated",
onReceiverChange
);
deviceManager.addEventListener(
"receiverDeviceMediaUpdated",
onReceiverChange
);
function onSelectorSelected(
ev: CustomEvent<ReceiverSelectionCast>
) {
logger.info("Selected receiver", ev.detail);
resolve({
actionType: ReceiverSelectionActionType.Cast,
receiverDevice: ev.detail.receiverDevice,
mediaType: ev.detail.mediaType
});
}
function onSelectorStop(ev: CustomEvent<ReceiverSelectionStop>) {
logger.info("Stopping receiver app...", ev.detail);
deviceManager.stopReceiverApp(ev.detail.receiverDevice.id);
resolve({
actionType: ReceiverSelectionActionType.Stop,
receiverDevice: ev.detail.receiverDevice
});
}
function onSelectorCancelled() {
logger.info("Cancelled receiver selection");
resolve(null);
}
function onSelectorError(ev: CustomEvent<string>) {
reject(ev.detail);
}
function onReceiverMessage(
ev: CustomEvent<ReceiverSelectorReceiverMessage>
) {
deviceManager.sendReceiverMessage(
ev.detail.deviceId,
ev.detail.message
);
}
function onMediaMessage(
ev: CustomEvent<ReceiverSelectorMediaMessage>
) {
deviceManager.sendMediaMessage(
ev.detail.deviceId,
ev.detail.message
);
}
ReceiverSelector.shared.addEventListener(
"selected",
onSelectorSelected
);
ReceiverSelector.shared.addEventListener("stop", onSelectorStop);
ReceiverSelector.shared.addEventListener(
"cancelled",
onSelectorCancelled
);
ReceiverSelector.shared.addEventListener("error", onSelectorError);
ReceiverSelector.shared.addEventListener(
"receiverMessage",
onReceiverMessage
);
ReceiverSelector.shared.addEventListener(
"mediaMessage",
onMediaMessage
);
ReceiverSelector.shared.addEventListener("close", removeListeners);
function removeListeners() {
ReceiverSelector.shared.removeEventListener(
"selected",
onSelectorSelected
);
ReceiverSelector.shared.removeEventListener(
"stop",
onSelectorStop
);
ReceiverSelector.shared.removeEventListener(
"cancelled",
onSelectorCancelled
);
ReceiverSelector.shared.removeEventListener(
"error",
onSelectorError
);
ReceiverSelector.shared.removeEventListener(
"receiverMessage",
onReceiverMessage
);
ReceiverSelector.shared.removeEventListener(
"mediaMessage",
onMediaMessage
);
ReceiverSelector.shared.removeEventListener(
"close",
removeListeners
);
deviceManager.removeEventListener(
"receiverDeviceUp",
onReceiverChange
);
deviceManager.removeEventListener(
"receiverDeviceDown",
onReceiverChange
);
deviceManager.removeEventListener(
"receiverDeviceUpdated",
onReceiverChange
);
deviceManager.removeEventListener(
"receiverDeviceMediaUpdated",
onReceiverChange
);
}
// Ensure status manager is initialized
await deviceManager.init();
let isRequestAppAudioCompatible: Optional<boolean>;
if (castInstance?.appId) {
const appTag = getAppTag(baseConfig, castInstance.appId);
isRequestAppAudioCompatible = appTag?.supports_audio_only;
}
ReceiverSelector.shared.open({
receiverDevices: deviceManager.getDevices(),
defaultMediaType,
availableMediaTypes,
appId: castInstance?.appId,
// Create page info
pageInfo: pageUrl
? {
url: pageUrl,
tabId: contextTabId,
frameId: contextFrameId,
sessionRequest: selectionOpts?.sessionRequest,
isRequestAppAudioCompatible
}
: undefined
});
});
}
}