MOBILE-3103 iframe: Manage orientation on iframes fullscreen

main
Pau Ferrer Ocaña 2021-10-04 16:40:06 +02:00
parent d8cc67eab1
commit a4f780b7b8
8 changed files with 70 additions and 25 deletions

22
package-lock.json generated
View File

@ -77,7 +77,6 @@
"cordova-plugin-media-capture": "^3.0.3", "cordova-plugin-media-capture": "^3.0.3",
"cordova-plugin-network-information": "^2.0.2", "cordova-plugin-network-information": "^2.0.2",
"cordova-plugin-prevent-override": "^1.0.0", "cordova-plugin-prevent-override": "^1.0.0",
"cordova-plugin-screen-orientation": "^3.0.2",
"cordova-plugin-splashscreen": "^6.0.0", "cordova-plugin-splashscreen": "^6.0.0",
"cordova-plugin-statusbar": "^2.4.3", "cordova-plugin-statusbar": "^2.4.3",
"cordova-plugin-whitelist": "^1.3.4", "cordova-plugin-whitelist": "^1.3.4",
@ -11201,18 +11200,6 @@
} }
} }
}, },
"node_modules/cordova-plugin-screen-orientation": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/cordova-plugin-screen-orientation/-/cordova-plugin-screen-orientation-3.0.2.tgz",
"integrity": "sha512-2w6CMC+HGvbhogJetalwGurL2Fx8DQCCPy3wlSZHN1/W7WoQ5n9ujVozcoKrY4VaagK6bxrPFih+ElkO8Uqfzg==",
"engines": {
"cordovaDependencies": {
"4.0.0": {
"cordova": ">100"
}
}
}
},
"node_modules/cordova-plugin-splashscreen": { "node_modules/cordova-plugin-splashscreen": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-6.0.0.tgz", "resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-6.0.0.tgz",
@ -12799,6 +12786,7 @@
"version": "0.1.13", "version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"devOptional": true,
"dependencies": { "dependencies": {
"iconv-lite": "^0.6.2" "iconv-lite": "^0.6.2"
} }
@ -12807,6 +12795,7 @@
"version": "0.6.2", "version": "0.6.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
"devOptional": true,
"dependencies": { "dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
}, },
@ -39888,11 +39877,6 @@
"resolved": "https://registry.npmjs.org/cordova-plugin-prevent-override/-/cordova-plugin-prevent-override-1.0.0.tgz", "resolved": "https://registry.npmjs.org/cordova-plugin-prevent-override/-/cordova-plugin-prevent-override-1.0.0.tgz",
"integrity": "sha512-/+3q5r4K5RahCpiYVmZQBjq10x4jj+6CMjYtZyx9jdMWeV+yFE+ItFcO1NeUAEWd2iHC5YPD0P2tHiHx5kscsw==" "integrity": "sha512-/+3q5r4K5RahCpiYVmZQBjq10x4jj+6CMjYtZyx9jdMWeV+yFE+ItFcO1NeUAEWd2iHC5YPD0P2tHiHx5kscsw=="
}, },
"cordova-plugin-screen-orientation": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/cordova-plugin-screen-orientation/-/cordova-plugin-screen-orientation-3.0.2.tgz",
"integrity": "sha512-2w6CMC+HGvbhogJetalwGurL2Fx8DQCCPy3wlSZHN1/W7WoQ5n9ujVozcoKrY4VaagK6bxrPFih+ElkO8Uqfzg=="
},
"cordova-plugin-splashscreen": { "cordova-plugin-splashscreen": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-6.0.0.tgz", "resolved": "https://registry.npmjs.org/cordova-plugin-splashscreen/-/cordova-plugin-splashscreen-6.0.0.tgz",
@ -41147,6 +41131,7 @@
"version": "0.1.13", "version": "0.1.13",
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz",
"integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==",
"devOptional": true,
"requires": { "requires": {
"iconv-lite": "^0.6.2" "iconv-lite": "^0.6.2"
}, },
@ -41155,6 +41140,7 @@
"version": "0.6.2", "version": "0.6.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
"integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==",
"devOptional": true,
"requires": { "requires": {
"safer-buffer": ">= 2.1.2 < 3.0.0" "safer-buffer": ">= 2.1.2 < 3.0.0"
} }

View File

@ -106,7 +106,6 @@
"cordova-plugin-media-capture": "^3.0.3", "cordova-plugin-media-capture": "^3.0.3",
"cordova-plugin-network-information": "^2.0.2", "cordova-plugin-network-information": "^2.0.2",
"cordova-plugin-prevent-override": "^1.0.0", "cordova-plugin-prevent-override": "^1.0.0",
"cordova-plugin-screen-orientation": "^3.0.2",
"cordova-plugin-splashscreen": "^6.0.0", "cordova-plugin-splashscreen": "^6.0.0",
"cordova-plugin-statusbar": "^2.4.3", "cordova-plugin-statusbar": "^2.4.3",
"cordova-plugin-whitelist": "^1.3.4", "cordova-plugin-whitelist": "^1.3.4",
@ -217,7 +216,6 @@
}, },
"cordova-plugin-network-information": {}, "cordova-plugin-network-information": {},
"@moodlehq/cordova-plugin-qrscanner": {}, "@moodlehq/cordova-plugin-qrscanner": {},
"cordova-plugin-screen-orientation": {},
"cordova-plugin-splashscreen": {}, "cordova-plugin-splashscreen": {},
"cordova-plugin-statusbar": {}, "cordova-plugin-statusbar": {},
"cordova-plugin-whitelist": {}, "cordova-plugin-whitelist": {},
@ -247,4 +245,4 @@
"optionalDependencies": { "optionalDependencies": {
"keytar": "^7.2.0" "keytar": "^7.2.0"
} }
} }

View File

@ -22,7 +22,7 @@
<core-navigation-bar [previous]="previousSco" [next]="nextSco" (action)="loadSco($event)"></core-navigation-bar> <core-navigation-bar [previous]="previousSco" [next]="nextSco" (action)="loadSco($event)"></core-navigation-bar>
<core-iframe *ngIf="loaded && src" [src]="src" [iframeWidth]="scormWidth" [iframeHeight]="scormHeight" <core-iframe *ngIf="loaded && src" [src]="src" [iframeWidth]="scormWidth" [iframeHeight]="scormHeight"
[showFullscreenOnToolbar]="true"> [showFullscreenOnToolbar]="true" [autoFullscreenOnRotate]="true">
</core-iframe> </core-iframe>
<p *ngIf="!src && errorMessage">{{ errorMessage | translate }}</p> <p *ngIf="!src && errorMessage">{{ errorMessage | translate }}</p>

View File

@ -63,7 +63,6 @@ export class AppComponent implements OnInit, AfterViewInit {
* - IAB events listening. * - IAB events listening.
* - Platform pause/resume subscriptions. * - Platform pause/resume subscriptions.
* - handleOpenURL and openWindowSafely. * - handleOpenURL and openWindowSafely.
* - Screen orientation events (probably it can be removed).
* - Back button registering to close modal first. * - Back button registering to close modal first.
* - Note: HideKeyboardFormAccessoryBar has been moved to config.xml. * - Note: HideKeyboardFormAccessoryBar has been moved to config.xml.
*/ */

View File

@ -22,7 +22,9 @@ import { CoreDomUtils } from '@services/utils/dom';
import { CoreUrlUtils } from '@services/utils/url'; import { CoreUrlUtils } from '@services/utils/url';
import { CoreIframeUtils } from '@services/utils/iframe'; import { CoreIframeUtils } from '@services/utils/iframe';
import { CoreUtils } from '@services/utils/utils'; import { CoreUtils } from '@services/utils/utils';
import { DomSanitizer } from '@singletons'; import { DomSanitizer, StatusBar } from '@singletons';
import { CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreScreen, CoreScreenOrientation } from '@services/screen';
@Component({ @Component({
selector: 'core-iframe', selector: 'core-iframe',
@ -39,6 +41,7 @@ export class CoreIframeComponent implements OnChanges, OnDestroy {
@Input() iframeHeight?: string; @Input() iframeHeight?: string;
@Input() allowFullscreen?: boolean | string; @Input() allowFullscreen?: boolean | string;
@Input() showFullscreenOnToolbar?: boolean | string; @Input() showFullscreenOnToolbar?: boolean | string;
@Input() autoFullscreenOnRotate?: boolean | string;
@Output() loaded: EventEmitter<HTMLIFrameElement> = new EventEmitter<HTMLIFrameElement>(); @Output() loaded: EventEmitter<HTMLIFrameElement> = new EventEmitter<HTMLIFrameElement>();
loading?: boolean; loading?: boolean;
@ -49,6 +52,7 @@ export class CoreIframeComponent implements OnChanges, OnDestroy {
initialized = false; initialized = false;
protected style?: HTMLStyleElement; protected style?: HTMLStyleElement;
protected orientationObs?: CoreEventObserver;
constructor() { constructor() {
this.loaded = new EventEmitter<HTMLIFrameElement>(); this.loaded = new EventEmitter<HTMLIFrameElement>();
@ -73,14 +77,23 @@ export class CoreIframeComponent implements OnChanges, OnDestroy {
this.iframeHeight = (this.iframeHeight && CoreDomUtils.formatPixelsSize(this.iframeHeight)) || '100%'; this.iframeHeight = (this.iframeHeight && CoreDomUtils.formatPixelsSize(this.iframeHeight)) || '100%';
this.allowFullscreen = CoreUtils.isTrueOrOne(this.allowFullscreen); this.allowFullscreen = CoreUtils.isTrueOrOne(this.allowFullscreen);
this.showFullscreenOnToolbar = CoreUtils.isTrueOrOne(this.showFullscreenOnToolbar); this.showFullscreenOnToolbar = CoreUtils.isTrueOrOne(this.showFullscreenOnToolbar);
this.autoFullscreenOnRotate = CoreUtils.isTrueOrOne(this.autoFullscreenOnRotate);
if (this.showFullscreenOnToolbar) { if (this.showFullscreenOnToolbar || this.autoFullscreenOnRotate) {
const shadow = const shadow =
iframe.closest('.ion-page')?.querySelector('ion-header ion-toolbar')?.shadowRoot; iframe.closest('.ion-page')?.querySelector('ion-header ion-toolbar')?.shadowRoot;
if (shadow) { if (shadow) {
this.style = document.createElement('style'); this.style = document.createElement('style');
shadow.appendChild(this.style); shadow.appendChild(this.style);
} }
if (this.autoFullscreenOnRotate) {
this.toggleFullscreen(CoreScreen.isLandscape);
this.orientationObs = CoreEvents.on(CoreEvents.ORIENTATION_CHANGE, (data) => {
this.toggleFullscreen(data.orientation == CoreScreenOrientation.LANDSCAPE);
});
}
} }
// Show loading only with external URLs. // Show loading only with external URLs.
@ -136,6 +149,7 @@ export class CoreIframeComponent implements OnChanges, OnDestroy {
*/ */
ngOnDestroy(): void { ngOnDestroy(): void {
this.toggleFullscreen(false); this.toggleFullscreen(false);
this.orientationObs?.off();
} }
/** /**
@ -148,6 +162,8 @@ export class CoreIframeComponent implements OnChanges, OnDestroy {
this.fullscreen = !this.fullscreen; this.fullscreen = !this.fullscreen;
} }
this.fullscreen ? StatusBar.hide() : StatusBar.show();
if (this.style) { if (this.style) {
// Done this way because of the shadow DOM. // Done this way because of the shadow DOM.
this.style.textContent = this.fullscreen this.style.textContent = this.fullscreen

View File

@ -16,4 +16,6 @@ import { CoreScreen } from '@services/screen';
export default function(): void { export default function(): void {
CoreScreen.watchViewport(); CoreScreen.watchViewport();
CoreScreen.watchOrientation();
} }

View File

@ -17,6 +17,7 @@ import { BehaviorSubject, Observable } from 'rxjs';
import { distinctUntilChanged, map } from 'rxjs/operators'; import { distinctUntilChanged, map } from 'rxjs/operators';
import { makeSingleton } from '@singletons'; import { makeSingleton } from '@singletons';
import { CoreEvents } from '@singletons/events';
/** /**
* Screen breakpoints. * Screen breakpoints.
@ -48,6 +49,14 @@ export enum CoreScreenLayout {
TABLET = 'tablet', TABLET = 'tablet',
} }
/**
* Screen orientation.
*/
export enum CoreScreenOrientation {
LANDSCAPE = 'landscape',
PORTRAIT = 'portrait',
}
/** /**
* Manage application screen. * Manage application screen.
*/ */
@ -93,6 +102,32 @@ export class CoreScreenService {
return this.layout === CoreScreenLayout.TABLET; return this.layout === CoreScreenLayout.TABLET;
} }
get orientation(): CoreScreenOrientation {
const mql = window.matchMedia('(orientation: portrait)');
return mql.matches ? CoreScreenOrientation.PORTRAIT : CoreScreenOrientation.LANDSCAPE;
}
get isPortrait(): boolean {
return this.orientation === CoreScreenOrientation.PORTRAIT;
}
get isLandscape(): boolean {
return this.orientation === CoreScreenOrientation.LANDSCAPE;
}
/**
* Watch orientation changes.
*/
watchOrientation(): void {
// Listen media orientation CSS queries.
window.matchMedia('(orientation: portrait)').addEventListener('change', (m) => {
const orientation = m.matches ? CoreScreenOrientation.PORTRAIT : CoreScreenOrientation.LANDSCAPE;
CoreEvents.trigger(CoreEvents.ORIENTATION_CHANGE, { orientation });
});
}
/** /**
* Watch viewport changes. * Watch viewport changes.
*/ */

View File

@ -19,6 +19,7 @@ import { CoreSite, CoreSiteInfoResponse, CoreSitePublicConfigResponse } from '@c
import { CoreFilepoolComponentFileEventData } from '@services/filepool'; import { CoreFilepoolComponentFileEventData } from '@services/filepool';
import { CoreNavigationOptions } from '@services/navigator'; import { CoreNavigationOptions } from '@services/navigator';
import { CoreCourseModuleCompletionData } from '@features/course/services/course-helper'; import { CoreCourseModuleCompletionData } from '@features/course/services/course-helper';
import { CoreScreenOrientation } from '@services/screen';
/** /**
* Observer instance to stop listening to an event. * Observer instance to stop listening to an event.
@ -55,6 +56,7 @@ export interface CoreEventsData {
[CoreEvents.COMPONENT_FILE_ACTION]: CoreFilepoolComponentFileEventData; [CoreEvents.COMPONENT_FILE_ACTION]: CoreFilepoolComponentFileEventData;
[CoreEvents.FILE_SHARED]: CoreEventFileSharedData; [CoreEvents.FILE_SHARED]: CoreEventFileSharedData;
[CoreEvents.APP_LAUNCHED_URL]: CoreEventAppLaunchedData; [CoreEvents.APP_LAUNCHED_URL]: CoreEventAppLaunchedData;
[CoreEvents.ORIENTATION_CHANGE]: CoreEventOrientationData;
} }
/* /*
@ -387,3 +389,10 @@ export type CoreEventFileSharedData = {
export type CoreEventAppLaunchedData = { export type CoreEventAppLaunchedData = {
url: string; url: string;
}; };
/**
* Data passed to ORIENTATION_CHANGE event.
*/
export type CoreEventOrientationData = {
orientation: CoreScreenOrientation;
};