Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[web] ScreenOrientation singleton #45304

Merged
merged 6 commits into from
Sep 18, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
75 changes: 75 additions & 0 deletions lib/web_ui/lib/src/engine/display.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:ui/ui.dart' as ui;

import '../engine.dart';
Expand Down Expand Up @@ -59,3 +61,76 @@ class EngineFlutterDisplay extends ui.Display {

double? _debugDevicePixelRatioOverride;
}

/// Controls the screen orientation using the browser's screen orientation API.
class ScreenOrientation {
const ScreenOrientation();

static ScreenOrientation get instance => _instance;
static const ScreenOrientation _instance = ScreenOrientation();

static const String lockTypeAny = 'any';
static const String lockTypeNatural = 'natural';
static const String lockTypeLandscape = 'landscape';
static const String lockTypePortrait = 'portrait';
static const String lockTypePortraitPrimary = 'portrait-primary';
static const String lockTypePortraitSecondary = 'portrait-secondary';
static const String lockTypeLandscapePrimary = 'landscape-primary';
static const String lockTypeLandscapeSecondary = 'landscape-secondary';

/// Sets preferred screen orientation.
///
/// Specifies the set of orientations the application interface can be
/// displayed in.
///
/// The [orientations] argument is a list of DeviceOrientation values.
/// The empty list uses Screen unlock api and causes the application to
/// defer to the operating system default.
///
/// See w3c screen api: https://www.w3.org/TR/screen-orientation/
Future<bool> setPreferredOrientation(List<dynamic> orientations) async {
final DomScreen? screen = domWindow.screen;
if (screen != null) {
final DomScreenOrientation? screenOrientation = screen.orientation;
if (screenOrientation != null) {
if (orientations.isEmpty) {
screenOrientation.unlock();
return true;
} else {
final String? lockType =
_deviceOrientationToLockType(orientations.first as String?);
if (lockType != null) {
try {
await screenOrientation.lock(lockType);
return true;
} catch (_) {
// On Chrome desktop an error with 'not supported on this device
// error' is fired.
return Future<bool>.value(false);
}
}
}
}
}
// API is not supported on this browser return false.
return false;
}

// Converts device orientation to w3c OrientationLockType enum.
//
// See also: https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/lock
static String? _deviceOrientationToLockType(String? deviceOrientation) {
switch (deviceOrientation) {
case 'DeviceOrientation.portraitUp':
return lockTypePortraitPrimary;
case 'DeviceOrientation.portraitDown':
return lockTypePortraitSecondary;
case 'DeviceOrientation.landscapeLeft':
return lockTypeLandscapePrimary;
case 'DeviceOrientation.landscapeRight':
return lockTypeLandscapeSecondary;
default:
return null;
}
}
}
74 changes: 0 additions & 74 deletions lib/web_ui/lib/src/engine/embedder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:ui/src/engine/safe_browser_api.dart';
import 'package:ui/ui.dart' as ui;

Expand Down Expand Up @@ -288,78 +286,6 @@ class FlutterViewEmbedder {
}
}

static const String orientationLockTypeAny = 'any';
static const String orientationLockTypeNatural = 'natural';
static const String orientationLockTypeLandscape = 'landscape';
static const String orientationLockTypePortrait = 'portrait';
static const String orientationLockTypePortraitPrimary = 'portrait-primary';
static const String orientationLockTypePortraitSecondary =
'portrait-secondary';
static const String orientationLockTypeLandscapePrimary = 'landscape-primary';
static const String orientationLockTypeLandscapeSecondary =
'landscape-secondary';

/// Sets preferred screen orientation.
///
/// Specifies the set of orientations the application interface can be
/// displayed in.
///
/// The [orientations] argument is a list of DeviceOrientation values.
/// The empty list uses Screen unlock api and causes the application to
/// defer to the operating system default.
///
/// See w3c screen api: https://www.w3.org/TR/screen-orientation/
Future<bool> setPreferredOrientation(List<dynamic> orientations) {
final DomScreen? screen = domWindow.screen;
if (screen != null) {
final DomScreenOrientation? screenOrientation = screen.orientation;
if (screenOrientation != null) {
if (orientations.isEmpty) {
screenOrientation.unlock();
return Future<bool>.value(true);
} else {
final String? lockType =
_deviceOrientationToLockType(orientations.first as String?);
if (lockType != null) {
final Completer<bool> completer = Completer<bool>();
try {
screenOrientation.lock(lockType).then((dynamic _) {
completer.complete(true);
}).catchError((dynamic error) {
// On Chrome desktop an error with 'not supported on this device
// error' is fired.
completer.complete(false);
});
} catch (_) {
return Future<bool>.value(false);
}
return completer.future;
}
}
}
}
// API is not supported on this browser return false.
return Future<bool>.value(false);
}

// Converts device orientation to w3c OrientationLockType enum.
//
// See also: https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/lock
static String? _deviceOrientationToLockType(String? deviceOrientation) {
switch (deviceOrientation) {
case 'DeviceOrientation.portraitUp':
return orientationLockTypePortraitPrimary;
case 'DeviceOrientation.portraitDown':
return orientationLockTypePortraitSecondary;
case 'DeviceOrientation.landscapeLeft':
return orientationLockTypeLandscapePrimary;
case 'DeviceOrientation.landscapeRight':
return orientationLockTypeLandscapeSecondary;
default:
return null;
}
}

/// Add an element as a global resource to be referenced by CSS.
///
/// This call create a global resource host element on demand and either
Expand Down
2 changes: 1 addition & 1 deletion lib/web_ui/lib/src/engine/platform_dispatcher.dart
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ class EnginePlatformDispatcher extends ui.PlatformDispatcher {
return;
case 'SystemChrome.setPreferredOrientations':
final List<dynamic> arguments = decoded.arguments as List<dynamic>;
flutterViewEmbedder.setPreferredOrientation(arguments).then((bool success) {
ScreenOrientation.instance.setPreferredOrientation(arguments).then((bool success) {
replyToPlatformMessage(
callback, codec.encodeSuccessEnvelope(success));
});
Expand Down
10 changes: 5 additions & 5 deletions lib/web_ui/test/engine/window_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -358,25 +358,25 @@ Future<void> testMain() async {
unlockCount = 0;

expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.portraitUp']), isTrue);
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypePortraitPrimary]);
expect(lockCalls, <String>[ScreenOrientation.lockTypePortraitPrimary]);
expect(unlockCount, 0);
lockCalls.clear();
unlockCount = 0;

expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.portraitDown']), isTrue);
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypePortraitSecondary]);
expect(lockCalls, <String>[ScreenOrientation.lockTypePortraitSecondary]);
expect(unlockCount, 0);
lockCalls.clear();
unlockCount = 0;

expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.landscapeLeft']), isTrue);
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypeLandscapePrimary]);
expect(lockCalls, <String>[ScreenOrientation.lockTypeLandscapePrimary]);
expect(unlockCount, 0);
lockCalls.clear();
unlockCount = 0;

expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.landscapeRight']), isTrue);
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypeLandscapeSecondary]);
expect(lockCalls, <String>[ScreenOrientation.lockTypeLandscapeSecondary]);
expect(unlockCount, 0);
lockCalls.clear();
unlockCount = 0;
Expand All @@ -389,7 +389,7 @@ Future<void> testMain() async {

simulateError = true;
expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.portraitDown']), isFalse);
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypePortraitSecondary]);
expect(lockCalls, <String>[ScreenOrientation.lockTypePortraitSecondary]);
expect(unlockCount, 0);

js_util.setProperty(domWindow, 'screen', original);
Expand Down