MOBILE-4669 scorm: Fix height problems in online player
This commit is contained in:
		
							parent
							
								
									bab16335d0
								
							
						
					
					
						commit
						20a39bbcb5
					
				| @ -980,10 +980,7 @@ | |||||||
|   "addon.mod_scorm.errorcreateofflineattempt": "local_moodlemobileapp", |   "addon.mod_scorm.errorcreateofflineattempt": "local_moodlemobileapp", | ||||||
|   "addon.mod_scorm.errordownloadscorm": "local_moodlemobileapp", |   "addon.mod_scorm.errordownloadscorm": "local_moodlemobileapp", | ||||||
|   "addon.mod_scorm.errorgetscorm": "local_moodlemobileapp", |   "addon.mod_scorm.errorgetscorm": "local_moodlemobileapp", | ||||||
|   "addon.mod_scorm.errorinvalidversion": "local_moodlemobileapp", |  | ||||||
|   "addon.mod_scorm.errornotdownloadable": "local_moodlemobileapp", |  | ||||||
|   "addon.mod_scorm.errornovalidsco": "local_moodlemobileapp", |   "addon.mod_scorm.errornovalidsco": "local_moodlemobileapp", | ||||||
|   "addon.mod_scorm.errorpackagefile": "local_moodlemobileapp", |  | ||||||
|   "addon.mod_scorm.errorsyncscorm": "local_moodlemobileapp", |   "addon.mod_scorm.errorsyncscorm": "local_moodlemobileapp", | ||||||
|   "addon.mod_scorm.exceededmaxattempts": "scorm", |   "addon.mod_scorm.exceededmaxattempts": "scorm", | ||||||
|   "addon.mod_scorm.failed": "scorm", |   "addon.mod_scorm.failed": "scorm", | ||||||
|  | |||||||
| @ -16,7 +16,7 @@ | |||||||
| <ion-content> | <ion-content> | ||||||
|     <core-loading [hideUntil]="loaded"> |     <core-loading [hideUntil]="loaded"> | ||||||
|         <core-iframe *ngIf="loaded" id="scorm_object" [src]="src" [iframeWidth]="scormWidth" [iframeHeight]="scormHeight" |         <core-iframe *ngIf="loaded" id="scorm_object" [src]="src" [iframeWidth]="scormWidth" [iframeHeight]="scormHeight" | ||||||
|             [showFullscreenOnToolbar]="true" [autoFullscreenOnRotate]="true" /> |             [showFullscreenOnToolbar]="true" [autoFullscreenOnRotate]="enableFullScreenOnRotate" (loaded)="iframeLoaded()" /> | ||||||
| 
 | 
 | ||||||
|         <p *ngIf="!src && errorMessage">{{ errorMessage | translate }}</p> |         <p *ngIf="!src && errorMessage">{{ errorMessage | translate }}</p> | ||||||
|     </core-loading> |     </core-loading> | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ | |||||||
| // See the License for the specific language governing permissions and
 | // See the License for the specific language governing permissions and
 | ||||||
| // limitations under the License.
 | // limitations under the License.
 | ||||||
| 
 | 
 | ||||||
| import { Component, OnInit, OnDestroy } from '@angular/core'; | import { Component, OnInit, OnDestroy, ViewChild } from '@angular/core'; | ||||||
| import { CoreNavigator } from '@services/navigator'; | import { CoreNavigator } from '@services/navigator'; | ||||||
| import { CoreSitesReadingStrategy } from '@services/sites'; | import { CoreSitesReadingStrategy } from '@services/sites'; | ||||||
| import { CoreDomUtils } from '@services/utils/dom'; | import { CoreDomUtils } from '@services/utils/dom'; | ||||||
| @ -31,6 +31,8 @@ import { Subscription } from 'rxjs'; | |||||||
| import { CoreNetwork } from '@services/network'; | import { CoreNetwork } from '@services/network'; | ||||||
| import { NgZone, Translate } from '@singletons'; | import { NgZone, Translate } from '@singletons'; | ||||||
| import { CoreError } from '@classes/errors/error'; | import { CoreError } from '@classes/errors/error'; | ||||||
|  | import { CoreWait } from '@singletons/wait'; | ||||||
|  | import { CoreIframeComponent } from '@components/iframe/iframe'; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Page that allows playing a SCORM in online, served from the server. |  * Page that allows playing a SCORM in online, served from the server. | ||||||
| @ -45,6 +47,8 @@ import { CoreError } from '@classes/errors/error'; | |||||||
| }) | }) | ||||||
| export default class AddonModScormOnlinePlayerPage implements OnInit, OnDestroy { | export default class AddonModScormOnlinePlayerPage implements OnInit, OnDestroy { | ||||||
| 
 | 
 | ||||||
|  |     @ViewChild(CoreIframeComponent) iframe?: CoreIframeComponent; | ||||||
|  | 
 | ||||||
|     scorm!: AddonModScormScorm; // The SCORM object.
 |     scorm!: AddonModScormScorm; // The SCORM object.
 | ||||||
|     loaded = false; // Whether the data has been loaded.
 |     loaded = false; // Whether the data has been loaded.
 | ||||||
|     src?: string; // Iframe src.
 |     src?: string; // Iframe src.
 | ||||||
| @ -53,6 +57,7 @@ export default class AddonModScormOnlinePlayerPage implements OnInit, OnDestroy | |||||||
|     scormHeight?: number; // Height applied to scorm iframe.
 |     scormHeight?: number; // Height applied to scorm iframe.
 | ||||||
|     cmId!: number; // Course module ID.
 |     cmId!: number; // Course module ID.
 | ||||||
|     courseId!: number; // Course ID.
 |     courseId!: number; // Course ID.
 | ||||||
|  |     enableFullScreenOnRotate = false; | ||||||
| 
 | 
 | ||||||
|     protected mode!: AddonModScormMode; // Mode to play the SCORM.
 |     protected mode!: AddonModScormMode; // Mode to play the SCORM.
 | ||||||
|     protected moduleUrl!: string; // Module URL.
 |     protected moduleUrl!: string; // Module URL.
 | ||||||
| @ -73,9 +78,9 @@ export default class AddonModScormOnlinePlayerPage implements OnInit, OnDestroy | |||||||
| 
 | 
 | ||||||
|                 if (!isOnline && wasOnline) { |                 if (!isOnline && wasOnline) { | ||||||
|                     // User lost connection while playing an online package. Show an error.
 |                     // User lost connection while playing an online package. Show an error.
 | ||||||
|                     CoreDomUtils.showErrorModal(new CoreError(Translate.instant('core.course.changesofflinemaybelost'))); // , {
 |                     CoreDomUtils.showErrorModal(new CoreError(Translate.instant('core.course.changesofflinemaybelost'), { | ||||||
|                         // title: Translate.instant('core.youreoffline'),
 |                         title: Translate.instant('core.youreoffline'), | ||||||
|                     // }));
 |                     })); | ||||||
| 
 | 
 | ||||||
|                     return; |                     return; | ||||||
|                 } |                 } | ||||||
| @ -114,7 +119,6 @@ export default class AddonModScormOnlinePlayerPage implements OnInit, OnDestroy | |||||||
|         } finally { |         } finally { | ||||||
|             this.loaded = true; |             this.loaded = true; | ||||||
|         } |         } | ||||||
| 
 |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
| @ -275,4 +279,21 @@ export default class AddonModScormOnlinePlayerPage implements OnInit, OnDestroy | |||||||
|         CoreEvents.trigger(CoreEvents.ACTIVITY_DATA_SENT, { module: 'scorm' }); |         CoreEvents.trigger(CoreEvents.ACTIVITY_DATA_SENT, { module: 'scorm' }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * SCORM iframe has been loaded. | ||||||
|  |      */ | ||||||
|  |     async iframeLoaded(): Promise<void> { | ||||||
|  |         // When using online player, some packages don't calculate the right height. Sending a 'resize' event doesn't fix it, but
 | ||||||
|  |         // changing the iframe size makes the SCORM recalculate the size.
 | ||||||
|  |         // Wait 1 second (to let inner iframes load) and then force full screen to make the SCORM recalculate the size.
 | ||||||
|  |         await CoreWait.wait(1000); | ||||||
|  | 
 | ||||||
|  |         if (this.isDestroyed) { | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.iframe?.toggleFullscreen(true); | ||||||
|  |         this.enableFullScreenOnRotate = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -159,13 +159,27 @@ Feature: Test basic usage of SCORM activity in app | |||||||
|     # And I go back in the app |     # And I go back in the app | ||||||
|     # Then I should find "1" within "Number of attempts you have made" "ion-item" in the app |     # Then I should find "1" within "Number of attempts you have made" "ion-item" in the app | ||||||
| 
 | 
 | ||||||
|   Scenario: Unsupported SCORM |   Scenario: SCORM 2004 works online | ||||||
|     Given the following "activities" exist: |     Given the following "activities" exist: | ||||||
|       | activity | name       | course | idnumber | packagefilepath                                                    | |       | activity | name       | course | idnumber | packagefilepath                                                    | | ||||||
|       | scorm    | SCORM 1.2 | C1     | scorm    | mod/scorm/tests/packages/RuntimeBasicCalls_SCORM20043rdEdition.zip | |       | scorm    | SCORM 2004 | C1     | scorm    | mod/scorm/tests/packages/RuntimeBasicCalls_SCORM20043rdEdition.zip | | ||||||
|     And I entered the course "Course 1" as "student1" in the app |     And I entered the course "Course 1" as "student1" in the app | ||||||
|     When I press "SCORM 1.2" in the app |     When I press "SCORM 2004" in the app | ||||||
|     Then I should find "Sorry, the application only supports SCORM 1.2." in the app |     Then I should be able to press "Enter" in the app | ||||||
|  | 
 | ||||||
|  |     When I switch network connection to offline | ||||||
|  |     Then I should find "This activity is not available offline" in the app | ||||||
|  |     And I should not be able to press "Enter" in the app | ||||||
|  | 
 | ||||||
|  |     When I switch network connection to wifi | ||||||
|  |     And I press "Enter" in the app | ||||||
|  |     And I press "Disable fullscreen" in the app | ||||||
|  |     Then I should not be able to press "TOC" in the app | ||||||
|  | 
 | ||||||
|  |     When I switch network connection to offline | ||||||
|  |     Then I should find "Any changes you make to this activity while offline may not be saved" in the app | ||||||
|  | 
 | ||||||
|  |     # TODO: When iframes are fixed, test that the iframe actually works. However, the Golf 2004 SCORM has some issues. | ||||||
| 
 | 
 | ||||||
|   Scenario: Hidden SCOs not displayed in TOC |   Scenario: Hidden SCOs not displayed in TOC | ||||||
|     Given the following "activities" exist: |     Given the following "activities" exist: | ||||||
|  | |||||||
| @ -64,6 +64,7 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { | |||||||
| 
 | 
 | ||||||
|     initialized = false; |     initialized = false; | ||||||
| 
 | 
 | ||||||
|  |     protected fullScreenInitialized = false; | ||||||
|     protected iframe?: HTMLIFrameElement; |     protected iframe?: HTMLIFrameElement; | ||||||
|     protected style?: HTMLStyleElement; |     protected style?: HTMLStyleElement; | ||||||
|     protected orientationObs?: CoreEventObserver; |     protected orientationObs?: CoreEventObserver; | ||||||
| @ -87,36 +88,6 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { | |||||||
| 
 | 
 | ||||||
|         this.initialized = true; |         this.initialized = true; | ||||||
| 
 | 
 | ||||||
|         if (this.showFullscreenOnToolbar || this.autoFullscreenOnRotate) { |  | ||||||
|             // Leave fullscreen when navigating.
 |  | ||||||
|             this.navSubscription = Router.events |  | ||||||
|                 .pipe(filter(event => event instanceof NavigationStart)) |  | ||||||
|                 .subscribe(async () => { |  | ||||||
|                     if (this.fullscreen) { |  | ||||||
|                         this.toggleFullscreen(false); |  | ||||||
|                     } |  | ||||||
|                 }); |  | ||||||
| 
 |  | ||||||
|             const shadow = |  | ||||||
|                 this.elementRef.nativeElement.closest('.ion-page')?.querySelector('ion-header ion-toolbar')?.shadowRoot; |  | ||||||
|             if (shadow) { |  | ||||||
|                 this.style = document.createElement('style'); |  | ||||||
|                 shadow.appendChild(this.style); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             if (this.autoFullscreenOnRotate) { |  | ||||||
|                 this.toggleFullscreen(CoreScreen.isLandscape); |  | ||||||
| 
 |  | ||||||
|                 this.orientationObs = CoreEvents.on(CoreEvents.ORIENTATION_CHANGE, (data) => { |  | ||||||
|                     if (this.isInHiddenPage()) { |  | ||||||
|                         return; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     this.toggleFullscreen(data.orientation == CoreScreenOrientation.LANDSCAPE); |  | ||||||
|                 }); |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Show loading only with external URLs.
 |         // Show loading only with external URLs.
 | ||||||
|         this.loading = !this.src || !CoreUrl.isLocalFileUrl(this.src); |         this.loading = !this.src || !CoreUrl.isLocalFileUrl(this.src); | ||||||
| 
 | 
 | ||||||
| @ -127,6 +98,72 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     /** | ||||||
|  |      * Configure fullscreen based on the inputs. | ||||||
|  |      */ | ||||||
|  |     protected configureFullScreen(): void { | ||||||
|  |         if (!this.showFullscreenOnToolbar && !this.autoFullscreenOnRotate) { | ||||||
|  |             // Full screen disabled, stop watchers if enabled.
 | ||||||
|  |             this.navSubscription?.unsubscribe(); | ||||||
|  |             this.orientationObs?.off(); | ||||||
|  |             this.style?.remove(); | ||||||
|  |             this.navSubscription = undefined; | ||||||
|  |             this.orientationObs = undefined; | ||||||
|  |             this.style = undefined; | ||||||
|  |             this.fullScreenInitialized = true; | ||||||
|  | 
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!this.navSubscription) { | ||||||
|  |             // Leave fullscreen when navigating.
 | ||||||
|  |             this.navSubscription = Router.events | ||||||
|  |                 .pipe(filter(event => event instanceof NavigationStart)) | ||||||
|  |                 .subscribe(async () => { | ||||||
|  |                     if (this.fullscreen) { | ||||||
|  |                         this.toggleFullscreen(false); | ||||||
|  |                     } | ||||||
|  |                 }); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!this.style) { | ||||||
|  |             const shadow = this.elementRef.nativeElement.closest('.ion-page')?.querySelector('ion-header ion-toolbar')?.shadowRoot; | ||||||
|  |             if (shadow) { | ||||||
|  |                 this.style = document.createElement('style'); | ||||||
|  |                 shadow.appendChild(this.style); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!this.autoFullscreenOnRotate) { | ||||||
|  |             this.orientationObs?.off(); | ||||||
|  |             this.orientationObs = undefined; | ||||||
|  |             this.fullScreenInitialized = true; | ||||||
|  | 
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (this.orientationObs) { | ||||||
|  |             this.fullScreenInitialized = true; | ||||||
|  | 
 | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         if (!this.fullScreenInitialized) { | ||||||
|  |             // Only change full screen value if it's being initialized.
 | ||||||
|  |             this.toggleFullscreen(CoreScreen.isLandscape); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         this.orientationObs = CoreEvents.on(CoreEvents.ORIENTATION_CHANGE, (data) => { | ||||||
|  |             if (this.isInHiddenPage()) { | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             this.toggleFullscreen(data.orientation == CoreScreenOrientation.LANDSCAPE); | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         this.fullScreenInitialized = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     /** |     /** | ||||||
|      * Initialize things related to the iframe element. |      * Initialize things related to the iframe element. | ||||||
|      */ |      */ | ||||||
| @ -170,6 +207,10 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { | |||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (!changes.src) { |         if (!changes.src) { | ||||||
|  |             if (changes.showFullscreenOnToolbar || changes.autoFullscreenOnRotate) { | ||||||
|  |                 this.configureFullScreen(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
| @ -212,6 +253,9 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { | |||||||
|         // Now that the URL has been set, initialize the iframe. Wait for the iframe to the added to the DOM.
 |         // Now that the URL has been set, initialize the iframe. Wait for the iframe to the added to the DOM.
 | ||||||
|         setTimeout(() => { |         setTimeout(() => { | ||||||
|             this.init(); |             this.init(); | ||||||
|  |             if (changes.showFullscreenOnToolbar || changes.autoFullscreenOnRotate) { | ||||||
|  |                 this.configureFullScreen(); | ||||||
|  |             } | ||||||
|         }); |         }); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -229,6 +273,12 @@ export class CoreIframeComponent implements OnChanges, OnDestroy { | |||||||
|         this.orientationObs?.off(); |         this.orientationObs?.off(); | ||||||
|         this.navSubscription?.unsubscribe(); |         this.navSubscription?.unsubscribe(); | ||||||
|         window.removeEventListener('message', this.messageListenerFunction); |         window.removeEventListener('message', this.messageListenerFunction); | ||||||
|  | 
 | ||||||
|  |         if (this.fullscreen) { | ||||||
|  |             // Make sure to leave fullscreen mode when the iframe is destroyed. This can happen if there's a race condition
 | ||||||
|  |             // between clicking back button and some code toggling the fullscreen on.
 | ||||||
|  |             this.toggleFullscreen(false); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     /** |     /** | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user