Skip to content

Commit

Permalink
feat(UI): Add video fullscreen support for iOS (#3853)
Browse files Browse the repository at this point in the history
Closes #3832
  • Loading branch information
Álvaro Velad Galván committed Jan 12, 2022
1 parent 3ff48cb commit 8d1b5e6
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 35 deletions.
3 changes: 3 additions & 0 deletions demo/main.js
Expand Up @@ -1124,6 +1124,9 @@ shakaDemo.Main = class {
if (document.fullscreenElement) {
document.exitFullscreen();
}
if (this.video_.webkitDisplayingFullscreen) {
this.video_.webkitExitFullscreen();
}
if (document.pictureInPictureElement) {
document.exitPictureInPicture();
}
Expand Down
15 changes: 13 additions & 2 deletions lib/ads/client_side_ad.js
Expand Up @@ -18,14 +18,18 @@ shaka.ads.ClientSideAd = class {
/**
* @param {!google.ima.Ad} imaAd
* @param {!google.ima.AdsManager} imaAdManager
* @param {HTMLMediaElement} video
*/
constructor(imaAd, imaAdManager) {
constructor(imaAd, imaAdManager, video) {
/** @private {google.ima.Ad} */
this.ad_ = imaAd;

/** @private {google.ima.AdsManager} */
this.manager_ = imaAdManager;

/** @private {HTMLMediaElement} */
this.video_ = video;

/** @private {boolean} */
this.isPaused_ = false;

Expand Down Expand Up @@ -176,7 +180,14 @@ shaka.ads.ClientSideAd = class {
* @export
*/
resize(width, height) {
const viewMode = document.fullscreenElement ?
let isInFullscreen = false;
const video = /** @type {HTMLVideoElement} */(this.video_);
if (document.fullscreenEnabled) {
isInFullscreen = !!document.fullscreenElement;
} else if (video.webkitSupportsFullscreen) {
isInFullscreen = video.webkitDisplayingFullscreen;
}
const viewMode = isInFullscreen ?
google.ima.ViewMode.FULLSCREEN : google.ima.ViewMode.NORMAL;
this.manager_.resize(width, height, viewMode);
}
Expand Down
26 changes: 22 additions & 4 deletions lib/ads/client_side_ad_manager.js
Expand Up @@ -181,7 +181,7 @@ shaka.ads.ClientSideAdManager = class {
this.addImaEventListeners_();

try {
const viewMode = document.fullscreenElement ?
const viewMode = this.isFullScreenEnabled_() ?
google.ima.ViewMode.FULLSCREEN : google.ima.ViewMode.NORMAL;

this.imaAdsManager_.init(this.video_.offsetWidth,
Expand All @@ -191,15 +191,15 @@ shaka.ads.ClientSideAdManager = class {
// because 'loadedmetadata' is sometimes called before the video resizes
// on some platforms (e.g. Safari).
this.eventManager_.listen(this.video_, 'loadeddata', () => {
const viewMode = document.fullscreenElement ?
const viewMode = this.isFullScreenEnabled_() ?
google.ima.ViewMode.FULLSCREEN : google.ima.ViewMode.NORMAL;
this.imaAdsManager_.resize(this.video_.offsetWidth,
this.video_.offsetHeight, viewMode);
});

if ('ResizeObserver' in window) {
this.resizeObserver_ = new ResizeObserver(() => {
const viewMode = document.fullscreenElement ?
const viewMode = this.isFullScreenEnabled_() ?
google.ima.ViewMode.FULLSCREEN : google.ima.ViewMode.NORMAL;
this.imaAdsManager_.resize(this.video_.offsetWidth,
this.video_.offsetHeight, viewMode);
Expand All @@ -221,6 +221,23 @@ shaka.ads.ClientSideAdManager = class {
}


/**
* @return {boolean}
* @private
*/
isFullScreenEnabled_() {
if (document.fullscreenEnabled) {
return !!document.fullscreenElement;
} else {
const video = /** @type {HTMLVideoElement} */(this.video_);
if (video.webkitSupportsFullscreen) {
return video.webkitDisplayingFullscreen;
}
}
return false;
}


/**
* @private
*/
Expand Down Expand Up @@ -389,7 +406,8 @@ shaka.ads.ClientSideAdManager = class {
'Should have an ads manager at this point!');

const imaAd = e.getAd();
this.ad_ = new shaka.ads.ClientSideAd(imaAd, this.imaAdsManager_);
this.ad_ = new shaka.ads.ClientSideAd(imaAd,
this.imaAdsManager_, this.video_);
const data = new Map()
.set('ad', this.ad_)
.set('sdkAdObject', imaAd)
Expand Down
88 changes: 65 additions & 23 deletions ui/controls.js
Expand Up @@ -553,33 +553,74 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
this.hideSettingsMenusTimer_.tickNow();
}

/**
* @return {boolean}
* @export
*/
isFullScreenSupported() {
if (document.fullscreenEnabled) {
return true;
}
const video = /** @type {HTMLVideoElement} */(this.localVideo_);
if (video.webkitSupportsFullscreen) {
return true;
}
return false;
}

/**
* @return {boolean}
* @export
*/
isFullScreenEnabled() {
if (document.fullscreenEnabled) {
return !!document.fullscreenElement;
}
const video = /** @type {HTMLVideoElement} */(this.localVideo_);
if (video.webkitSupportsFullscreen) {
return video.webkitDisplayingFullscreen;
}
return false;
}

/** @export */
async toggleFullScreen() {
if (document.fullscreenElement) {
if (screen.orientation) {
screen.orientation.unlock();
}
await document.exitFullscreen();
} else {
// If we are in PiP mode, leave PiP mode first.
try {
if (document.pictureInPictureElement) {
await document.exitPictureInPicture();
if (document.fullscreenEnabled) {
if (document.fullscreenElement) {
if (screen.orientation) {
screen.orientation.unlock();
}
await this.videoContainer_.requestFullscreen({navigationUI: 'hide'});
if (this.config_.forceLandscapeOnFullscreen && screen.orientation) {
try {
// Locking to 'landscape' should let it be either
// 'landscape-primary' or 'landscape-secondary' as appropriate.
await screen.orientation.lock('landscape');
} catch (error) {
// If screen.orientation.lock does not work on a device, it will
// be rejected with an error. Suppress that error.
await document.exitFullscreen();
} else {
// If we are in PiP mode, leave PiP mode first.
try {
if (document.pictureInPictureElement) {
await document.exitPictureInPicture();
}
await this.videoContainer_.requestFullscreen({navigationUI: 'hide'});
if (this.config_.forceLandscapeOnFullscreen && screen.orientation) {
try {
// Locking to 'landscape' should let it be either
// 'landscape-primary' or 'landscape-secondary' as appropriate.
await screen.orientation.lock('landscape');
} catch (error) {
// If screen.orientation.lock does not work on a device, it will
// be rejected with an error. Suppress that error.
}
}
} catch (error) {
this.dispatchEvent(new shaka.util.FakeEvent(
'error', (new Map()).set('detail', error)));
}
}
} else {
const video = /** @type {HTMLVideoElement} */(this.localVideo_);
if (video.webkitSupportsFullscreen) {
if (video.webkitDisplayingFullscreen) {
video.webkitExitFullscreen();
} else {
video.webkitEnterFullscreen();
}
} catch (error) {
this.dispatchEvent(new shaka.util.FakeEvent(
'error', (new Map()).set('detail', error)));
}
}
}
Expand Down Expand Up @@ -699,7 +740,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
});

this.eventManager_.listen(this.controlsContainer_, 'dblclick', () => {
if (this.config_.doubleClickForFullscreen && document.fullscreenEnabled) {
if (this.config_.doubleClickForFullscreen &&
this.isFullScreenSupported()) {
this.toggleFullScreen();
}
});
Expand Down
32 changes: 26 additions & 6 deletions ui/fullscreen_button.js
Expand Up @@ -28,16 +28,16 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
constructor(parent, controls) {
super(parent, controls);

/** @private {HTMLMediaElement} */
this.localVideo_ = this.controls.getLocalVideo();

/** @private {!HTMLButtonElement} */
this.button_ = shaka.util.Dom.createButton();
this.button_.classList.add('shaka-fullscreen-button');
this.button_.classList.add('material-icons-round');
this.button_.classList.add('shaka-tooltip');

// Don't show the button if fullscreen is not supported
if (!document.fullscreenEnabled) {
this.button_.classList.add('shaka-hidden');
}
this.checkSupport_();

this.button_.textContent = shaka.ui.Enums.MaterialDesignIcons.FULLSCREEN;
this.parent.appendChild(this.button_);
Expand All @@ -61,14 +61,34 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
this.updateIcon_();
this.updateAriaLabel_();
});

this.eventManager.listen(this.localVideo_, 'loadedmetadata', () => {
this.checkSupport_();
});

this.eventManager.listen(this.localVideo_, 'loadeddata', () => {
this.checkSupport_();
});
}

/**
* @private
*/
checkSupport_() {
// Don't show the button if fullscreen is not supported
if (!this.controls.isFullScreenSupported()) {
this.button_.classList.add('shaka-hidden');
} else {
this.button_.classList.remove('shaka-hidden');
}
}

/**
* @private
*/
updateAriaLabel_() {
const LocIds = shaka.ui.Locales.Ids;
const label = document.fullscreenElement ?
const label = this.controls.isFullScreenEnabled() ?
LocIds.EXIT_FULL_SCREEN : LocIds.FULL_SCREEN;

this.button_.ariaLabel = this.localization.resolve(label);
Expand All @@ -79,7 +99,7 @@ shaka.ui.FullscreenButton = class extends shaka.ui.Element {
*/
updateIcon_() {
this.button_.textContent =
document.fullscreenElement ?
this.controls.isFullScreenEnabled() ?
shaka.ui.Enums.MaterialDesignIcons.EXIT_FULLSCREEN :
shaka.ui.Enums.MaterialDesignIcons.FULLSCREEN;
}
Expand Down

0 comments on commit 8d1b5e6

Please sign in to comment.