diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/landscape-warning/landscape-warning.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/landscape-warning/landscape-warning.component.html new file mode 100644 index 00000000..6bc3cea0 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/landscape-warning/landscape-warning.component.html @@ -0,0 +1,7 @@ + +
+
+ screen_rotation + {{ 'ROOM.LANDSCAPE_WARNING' | translate }} +
+
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/landscape-warning/landscape-warning.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/landscape-warning/landscape-warning.component.scss new file mode 100644 index 00000000..91cde86a --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/landscape-warning/landscape-warning.component.scss @@ -0,0 +1,33 @@ +#landscape-warning { + width: 100%; + height: 100%; + background-color: var(--ov-background-color); + opacity: 95%; + align-content: space-evenly; + text-align: center; + color: var(--ov-text-primary-color); + + .warning-message { + display: inline-grid; + display: -moz-inline-grid; + place-items: center; + } + + mat-icon { + width: 50px; + height: 50px; + font-size: 50px; + margin: auto; + margin-bottom: 10px; + animation: boomerang 1.2s ease-in-out infinite alternate; + + @keyframes boomerang { + from { + transform: rotate(0deg); + } + to { + transform: rotate(45deg); + } + } + } +} diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/landscape-warning/landscape-warning.component.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/landscape-warning/landscape-warning.component.ts new file mode 100644 index 00000000..de64e767 --- /dev/null +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/landscape-warning/landscape-warning.component.ts @@ -0,0 +1,20 @@ +import { animate, style, transition, trigger } from '@angular/animations'; +import { Component } from '@angular/core'; + +/** + * Component to display a landscape orientation warning on mobile devices. + * @internal + */ +@Component({ + selector: 'ov-landscape-warning', + templateUrl: './landscape-warning.component.html', + styleUrl: './landscape-warning.component.scss', + standalone: false, + animations: [ + trigger('inOutAnimation', [ + transition(':enter', [style({ opacity: 0 }), animate('200ms', style({ opacity: 1 }))]), + transition(':leave', [animate('200ms', style({ opacity: 0 }))]) + ]) + ] +}) +export class LandscapeWarningComponent {} diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.html index 367e3fe3..db1f58c4 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/pre-join/pre-join.component.html @@ -1,123 +1,128 @@ -
- -
- -
- - - @if (isLoading) { -
-
- - {{ 'PREJOIN.PREPARING' | translate }} -
+@if (viewportService.shouldShowLandscapeWarning()) { + +} @else { +
+ +
+
- } @else { - -
- -
- -
-
-
- - - -
-
-
- - + + @if (isLoading) { +
+
+ + {{ 'PREJOIN.PREPARING' | translate }} +
+
+ } @else { + +
+ +
+ +
+
+
+ + + + +
+
+
+ + +
+ +
+ + +
-
- - -
+ + @if (backgroundEffectEnabled && hasVideoDevices) { +
+ +
+ }
- - - @if (backgroundEffectEnabled && hasVideoDevices) { -
- -
- }
+ + @if (showBackgroundPanel) { +
+ + +
+ } @else { + +
+ +
+ + +
+ + +
+ error_outline + {{ _error }} +
+ + +
+ +
+
+ }
- - @if (showBackgroundPanel) { -
- -
- } @else { - -
- -
- - -
- - -
- error_outline - {{ _error }} -
- - -
- -
-
- }
-
- } -
+ } +
+} diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.html b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.html index eaf7dc2e..499a6866 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.html +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.html @@ -4,14 +4,8 @@ {{ 'ROOM.JOINING' | translate }}
} @else { - @if (viewportService.isMobile() && viewportService.orientation() === 'landscape') { - -
-
- screen_rotation - {{ 'ROOM.LANDSCAPE_WARNING' | translate }} -
-
+ @if (viewportService.shouldShowLandscapeWarning()) { + }
diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.scss b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.scss index 820d8d9c..be5731f7 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.scss +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/components/session/session.component.scss @@ -4,39 +4,6 @@ height: 100%; } -#landscape-warning { - width: 100%; - height: 100%; - background-color: var(--ov-background-color); - opacity: 95%; - align-content: space-evenly; - text-align: center; - color: var(--ov-text-primary-color); - - .warning-message { - display: inline-grid; - display: -moz-inline-grid; - place-items: center; - } - - mat-icon { - width: 50px; - height: 50px; - font-size: 50px; - margin: auto; - margin-bottom: 10px; - animation: boomerang 1.2s ease-in-out infinite alternate; - - @keyframes boomerang { - from { - transform: rotate(0deg); - } - to { - transform: rotate(45deg); - } - } - } -} #spinner { position: absolute; top: 40%; diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/models/viewport.model.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/models/viewport.model.ts index 0e29bb95..acaba29b 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/models/viewport.model.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/models/viewport.model.ts @@ -24,4 +24,7 @@ export interface ViewportInfo { isDesktop: boolean; isWide: boolean; isTouchDevice: boolean; + isPhysicalMobile: boolean; + isPhysicalTablet: boolean; + shouldShowLandscapeWarning: boolean; } diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/openvidu-components-angular-ui.module.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/openvidu-components-angular-ui.module.ts index eaf85a66..f3271a29 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/openvidu-components-angular-ui.module.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/openvidu-components-angular-ui.module.ts @@ -47,6 +47,7 @@ import { ApiDirectiveModule } from './directives/api/api.directive.module'; import { OpenViduComponentsDirectiveModule } from './directives/template/openvidu-components-angular.directive.module'; import { AppMaterialModule } from './openvidu-components-angular.material.module'; import { ThemeSelectorComponent } from './components/settings/theme-selector/theme-selector.component'; +import { LandscapeWarningComponent } from './components/landscape-warning/landscape-warning.component'; const publicComponents = [ AdminDashboardComponent, @@ -81,7 +82,8 @@ const privateComponents = [ LangSelectorComponent, ToolbarMediaButtonsComponent, ToolbarPanelButtonsComponent, - ThemeSelectorComponent + ThemeSelectorComponent, + LandscapeWarningComponent ]; @NgModule({ diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/platform/platform.service.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/platform/platform.service.ts index 4d2c7390..712d6b87 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/platform/platform.service.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/platform/platform.service.ts @@ -22,17 +22,60 @@ export class PlatformService { } /** - * Detect Android Mobile + * Returns true if the device is physically a mobile device (iPhone, Android phone) + * This method is orientation-independent and hardware-based + */ + isPhysicalMobile(): boolean { + return this.isIPhone() || this.isAndroidPhone(); + } + + /** + * Returns true if the device is physically a tablet (iPad, Android tablet) + */ + isPhysicalTablet(): boolean { + return this.isIPad() || this.isAndroidTablet(); + } + + /** + * Detect Android phone specifically (not tablet) + */ + isAndroidPhone(): boolean { + return /\b(\w*Android\w*)\b/.test(this.userAgent) && /\b(\w*Mobile\w*)\b/.test(this.userAgent); + } + + /** + * Detect Android tablet specifically + */ + isAndroidTablet(): boolean { + return /\b(\w*Android\w*)\b/.test(this.userAgent) && !/\b(\w*Mobile\w*)\b/.test(this.userAgent); + } + + /** + * Detect Android Mobile (legacy method for compatibility) */ isAndroid(): boolean { - return /\b(\w*Android\w*)\b/.test(this.userAgent) && /\b(\w*Mobile\w*)\b/.test(this.userAgent); + return this.isAndroidPhone() || this.isAndroidTablet(); + } + + /** + * Detect iPhone specifically + */ + isIPhone(): boolean { + return /\biPhone\b/.test(this.userAgent) && /\bMobile\b/.test(this.userAgent); + } + + /** + * Detect iPad specifically + */ + isIPad(): boolean { + return /\bMacintosh\b/.test(this.userAgent) && 'ontouchend' in document; } /** * Detect iOS device (iPhone or iPad) */ isIos(): boolean { - return this.isIosDevice(this.userAgent); + return this.isIPhone() || this.isIPad(); } /** @@ -43,12 +86,42 @@ export class PlatformService { } /** - * Detect if the device is an iPhone or iPad + * Get the maximum screen dimension (useful for detecting device capabilities) */ - private isIosDevice(userAgent: string): boolean { - const isIPad = /\bMacintosh\b/.test(userAgent) && 'ontouchend' in document; - const isIPhone = /\biPhone\b/.test(userAgent) && /\bMobile\b/.test(userAgent); - return isIPad || isIPhone; + getMaxScreenDimension(): number { + if (typeof screen === 'undefined') return 1024; + return Math.max(screen.width, screen.height); + } + + /** + * Get the minimum screen dimension + */ + getMinScreenDimension(): number { + if (typeof screen === 'undefined') return 768; + return Math.min(screen.width, screen.height); + } + + /** + * Enhanced mobile detection that considers physical device characteristics + * This is orientation-independent and more reliable for landscape warnings + */ + isPhysicalMobileDevice(): boolean { + // First check: User agent based detection (most reliable) + if (this.isPhysicalMobile()) { + return true; + } + + // Second check: Screen dimensions for edge cases + // Most mobile devices have a max dimension <= 950px even in landscape + const maxDimension = this.getMaxScreenDimension(); + const minDimension = this.getMinScreenDimension(); + + // If touch device with small screen dimensions, likely mobile + if (this.isTouchDevice() && maxDimension <= 950 && minDimension <= 500) { + return true; + } + + return false; } // ===== Browser Detection ===== diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/viewport/viewport.service.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/viewport/viewport.service.ts index af2b9d7b..e0203739 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/viewport/viewport.service.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/lib/services/viewport/viewport.service.ts @@ -49,6 +49,17 @@ export class ViewportService implements OnDestroy { */ readonly isTouchDevice = computed(() => this.platform.isTouchDevice()); + /** + * Whether device is physically a mobile device (orientation-independent) + * This uses hardware detection, not just screen size + */ + readonly isPhysicalMobile = computed(() => this.platform.isPhysicalMobileDevice()); + + /** + * Whether device is physically a tablet (orientation-independent) + */ + readonly isPhysicalTablet = computed(() => this.platform.isPhysicalTablet()); + /** * Current viewport size category */ @@ -68,7 +79,8 @@ export class ViewportService implements OnDestroy { }); /** - * Whether current viewport is mobile size + * Whether current viewport is mobile size (legacy method) + * For landscape warnings, use isPhysicalMobile instead */ readonly isMobile = computed(() => this.viewportSize() === 'mobile' && this.platform.isTouchDevice()); @@ -77,6 +89,14 @@ export class ViewportService implements OnDestroy { */ readonly isTablet = computed(() => this.viewportSize() === 'tablet' && this.platform.isTouchDevice()); + /** + * Whether device should show mobile landscape warning + * This is orientation-independent and hardware-based detection + */ + readonly shouldShowLandscapeWarning = computed(() => + this.isPhysicalMobile() && this.orientation() === 'landscape' + ); + /** * Whether current viewport is desktop size */ @@ -119,7 +139,10 @@ export class ViewportService implements OnDestroy { isTablet: this.isTablet(), isDesktop: this.isDesktop(), isWide: this.isWide(), - isTouchDevice: this.isTouchDevice() + isTouchDevice: this.isTouchDevice(), + isPhysicalMobile: this.isPhysicalMobile(), + isPhysicalTablet: this.isPhysicalTablet(), + shouldShowLandscapeWarning: this.shouldShowLandscapeWarning() })); // ==== PUBLIC UTILITY METHODS ==== diff --git a/openvidu-components-angular/projects/openvidu-components-angular/src/public-api.ts b/openvidu-components-angular/projects/openvidu-components-angular/src/public-api.ts index 2fdc29cb..f3e1c671 100644 --- a/openvidu-components-angular/projects/openvidu-components-angular/src/public-api.ts +++ b/openvidu-components-angular/projects/openvidu-components-angular/src/public-api.ts @@ -18,6 +18,7 @@ export * from './lib/components/toolbar/toolbar.component'; export * from './lib/components/toolbar/toolbar-media-buttons/toolbar-media-buttons.component'; export * from './lib/components/toolbar/toolbar-panel-buttons/toolbar-panel-buttons.component'; export * from './lib/components/videoconference/videoconference.component'; +export * from './lib/components/landscape-warning/landscape-warning.component'; export * from './lib/config/openvidu-components-angular.config'; // Directives export * from './lib/directives/api/activities-panel.directive';