Skip to content

Commit 0e27c2f

Browse files
authored
Merge pull request #1716 from EnsembleUI/device_orientation
Device orientation
2 parents fb714ef + 46aba68 commit 0e27c2f

File tree

4 files changed

+96
-12
lines changed

4 files changed

+96
-12
lines changed

modules/ensemble/lib/framework/bindings.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ abstract class BindingSource {
4444
String binding, DataContext dataContext) {
4545
RegExp variableNameRegex = RegExp('^[0-9a-z_]+', caseSensitive: false);
4646

47+
// Check for device bindings (both patterns)
48+
String deviceExpr = 'ensemble.device.';
49+
if (binding.startsWith(deviceExpr)) {
50+
// For ensemble.device.property pattern
51+
String property = binding.substring(deviceExpr.length);
52+
return DeviceBindingSource(property);
53+
} else if (binding.startsWith('device.')) {
54+
// For device.property pattern
55+
String property = binding.substring('device.'.length);
56+
return DeviceBindingSource(property);
57+
}
58+
4759
// bindable storage
4860
String storageExpr = 'ensemble.storage.';
4961
String userExpr = 'ensemble.user.';
@@ -121,6 +133,18 @@ abstract class BindingSource {
121133
String variable = expression.substring(2, expression.length - 1).trim();
122134
RegExp variableNameRegex = RegExp('^[0-9a-z_]+', caseSensitive: false);
123135

136+
// Check for device bindings (both patterns)
137+
String deviceExpr = 'ensemble.device.';
138+
if (variable.startsWith(deviceExpr)) {
139+
// For ensemble.device.property pattern
140+
String property = variable.substring(deviceExpr.length);
141+
return DeviceBindingSource(property);
142+
} else if (variable.startsWith('device.')) {
143+
// For device.property pattern
144+
String property = variable.substring('device.'.length);
145+
return DeviceBindingSource(property);
146+
}
147+
124148
// storage bindable
125149
String storageExpr = 'ensemble.storage.';
126150
String userExpr = 'ensemble.user.';
@@ -202,6 +226,11 @@ class StorageBindingSource extends BindingSource {
202226
StorageBindingSource(super.modelId);
203227
}
204228

229+
/// bindable source backed by device
230+
class DeviceBindingSource extends BindingSource {
231+
DeviceBindingSource(super.modelId);
232+
}
233+
205234
/// TODO: consolidate this with StorageBindingSource
206235
class SystemStorageBindingSource extends BindingSource {
207236
SystemStorageBindingSource(super.modelId, {required this.storagePrefix});

modules/ensemble/lib/framework/data_context.dart

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -88,15 +88,19 @@ class DataContext implements Context {
8888
_contextMap['auth'] = GetIt.instance<AuthContextManager>();
8989
}
9090

91-
_addLegacyDataContext();
91+
_addLegacyDataContext(buildContext);
9292
}
9393

94-
// For backward compatibility
95-
void _addLegacyDataContext() {
94+
9695
// device is a common name. If user already uses that, don't override it
9796
// This is now in ensemble.device.*
97+
void _addLegacyDataContext(BuildContext? newBuildContext) {
9898
if (_contextMap['device'] == null) {
99-
_contextMap['device'] = Device();
99+
_contextMap['device'] = Device(newBuildContext);
100+
} else {
101+
// If device exists, update its context
102+
final device = _contextMap['device'];
103+
device.updateContext(newBuildContext);
100104
}
101105
}
102106

@@ -468,7 +472,7 @@ class NativeInvokable extends ActionInvokable {
468472
'user': () => UserInfo(),
469473
'formatter': () => Formatter(),
470474
'utils': () => _EnsembleUtils(),
471-
'device': () => Device(),
475+
'device': () => Device(buildContext),
472476
'version': () => _cache['version'],
473477
};
474478
}

modules/ensemble/lib/framework/device.dart

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:async';
12
import 'dart:core';
23
import 'dart:io';
34
import 'dart:developer';
@@ -14,24 +15,64 @@ import 'package:ensemble_ts_interpreter/invokables/invokable.dart';
1415
import 'package:flutter/cupertino.dart';
1516
import 'package:flutter/foundation.dart';
1617
import 'package:flutter/services.dart';
18+
import 'package:flutter/widgets.dart';
1719
import 'package:get_it/get_it.dart';
1820
import 'package:intl/intl.dart';
1921

20-
/// get device information as well as requesting device permissions
2122
class Device
2223
with
2324
Invokable,
2425
MediaQueryCapability,
2526
LocationCapability,
26-
DeviceInfoCapability {
27+
DeviceInfoCapability,
28+
WidgetsBindingObserver {
2729
static final Device _instance = Device._internal();
30+
static late BuildContext context;
2831

29-
Device._internal();
32+
Device._internal() {
33+
WidgetsBinding.instance.addObserver(this);
34+
}
3035

31-
factory Device() {
36+
factory Device([BuildContext? buildContext]) {
37+
if (buildContext != null) {
38+
context = buildContext;
39+
}
3240
return _instance;
3341
}
3442

43+
// method to update context
44+
void updateContext(BuildContext? newContext) {
45+
if (newContext != null) {
46+
context = newContext;
47+
}
48+
}
49+
50+
@override
51+
void didChangeMetrics() {
52+
WidgetsBinding.instance
53+
.addPostFrameCallback((_) => _handleMediaQueryChange());
54+
}
55+
56+
void _handleMediaQueryChange() {
57+
final newData = MediaQuery.of(context);
58+
59+
// Compare with existing static data
60+
if (MediaQueryCapability.data?.orientation != newData.orientation ||
61+
MediaQueryCapability.data?.size != newData.size) {
62+
MediaQueryCapability.data = newData;
63+
64+
// Dispatch individual property changes
65+
ScreenController().dispatchDeviceChanges(context, 'width', screenWidth);
66+
ScreenController().dispatchDeviceChanges(context, 'height', screenHeight);
67+
ScreenController()
68+
.dispatchDeviceChanges(context, 'orientation', screenOrientation);
69+
ScreenController()
70+
.dispatchDeviceChanges(context, 'safeAreaTop', safeAreaTop);
71+
ScreenController()
72+
.dispatchDeviceChanges(context, 'safeAreaBottom', safeAreaBottom);
73+
}
74+
}
75+
3576
@override
3677
Map<String, Function> getters() {
3778
return {
@@ -42,6 +83,7 @@ class Device
4283
// Media Query
4384
"width": () => screenWidth,
4485
"height": () => screenHeight,
86+
"orientation": () => screenOrientation,
4587
"safeAreaTop": () => safeAreaTop,
4688
"safeAreaBottom": () => safeAreaBottom,
4789

@@ -73,9 +115,7 @@ class Device
73115
}
74116

75117
@override
76-
Map<String, Function> setters() {
77-
return {};
78-
}
118+
Map<String, Function> setters() => {};
79119

80120
void openAppSettings([String? target]) {
81121
final settingType =
@@ -96,6 +136,7 @@ mixin MediaQueryCapability {
96136

97137
int get screenWidth => _getData().size.width.toInt();
98138
int get screenHeight => _getData().size.height.toInt();
139+
String get screenOrientation => _getData().orientation.toString();
99140
int get safeAreaTop => _getData().padding.top.toInt();
100141
int get safeAreaBottom => _getData().padding.bottom.toInt();
101142
}

modules/ensemble/lib/screen_controller.dart

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -578,6 +578,16 @@ class ScreenController {
578578
}
579579
}
580580

581+
void dispatchDeviceChanges(
582+
BuildContext context, String property, dynamic value) {
583+
ScopeManager? scopeManager = getScopeManager(context);
584+
585+
if (scopeManager != null) {
586+
scopeManager
587+
.dispatch(ModelChangeEvent(DeviceBindingSource(property), value));
588+
}
589+
}
590+
581591
void dispatchSystemStorageChanges(
582592
BuildContext context, String key, dynamic value,
583593
{required String storagePrefix}) {

0 commit comments

Comments
 (0)