Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ shoryukenn <[email protected]>
SOTEC GmbH & Co. KG <[email protected]>
Hidenori Matsubayashi <[email protected]>
Sarbagya Dhaubanjar <[email protected]>
Callum Moffat <[email protected]>
Original file line number Diff line number Diff line change
Expand Up @@ -814,7 +814,8 @@ - (void)goToApplicationLifecycle:(nonnull NSString*)state {
// in the status bar area are available to framework code. The change type (optional) of the faked
// touch is specified in the second argument.
- (void)dispatchTouches:(NSSet*)touches
pointerDataChangeOverride:(flutter::PointerData::Change*)overridden_change {
pointerDataChangeOverride:(flutter::PointerData::Change*)overridden_change
event:(UIEvent*)event {
if (!_engine) {
return;
}
Expand Down Expand Up @@ -925,31 +926,42 @@ - (void)dispatchTouches:(NSSet*)touches
pointer_data.orientation = [touch azimuthAngleInView:nil] - M_PI_2;
}

if (@available(iOS 13.4, *)) {
if (event != nullptr) {
pointer_data.buttons = (((event.buttonMask & UIEventButtonMaskPrimary) > 0)
? flutter::PointerButtonMouse::kPointerButtonMousePrimary
: 0) |
(((event.buttonMask & UIEventButtonMaskSecondary) > 0)
? flutter::PointerButtonMouse::kPointerButtonMouseSecondary
: 0);
}
}

packet->SetPointerData(pointer_index++, pointer_data);
}

[_engine.get() dispatchPointerDataPacket:std::move(packet)];
}

- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
[self dispatchTouches:touches pointerDataChangeOverride:nullptr];
[self dispatchTouches:touches pointerDataChangeOverride:nullptr event:event];
}

- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
[self dispatchTouches:touches pointerDataChangeOverride:nullptr];
[self dispatchTouches:touches pointerDataChangeOverride:nullptr event:event];
}

- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
[self dispatchTouches:touches pointerDataChangeOverride:nullptr];
[self dispatchTouches:touches pointerDataChangeOverride:nullptr event:event];
}

- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
[self dispatchTouches:touches pointerDataChangeOverride:nullptr];
[self dispatchTouches:touches pointerDataChangeOverride:nullptr event:event];
}

- (void)forceTouchesCancelled:(NSSet*)touches {
flutter::PointerData::Change cancel = flutter::PointerData::Change::kCancel;
[self dispatchTouches:touches pointerDataChangeOverride:&cancel];
[self dispatchTouches:touches pointerDataChangeOverride:&cancel event:nullptr];
}

#pragma mark - Handle view resizing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
6816DBA42318358200A51400 /* GoldenTestManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 6816DBA32318358200A51400 /* GoldenTestManager.m */; };
68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */; };
68D4017D2564859300ECD91A /* ContinuousTexture.m in Sources */ = {isa = PBXBuildFile; fileRef = 68D4017C2564859300ECD91A /* ContinuousTexture.m */; };
F26F15B8268B6B5600EC54D3 /* iPadGestureTests.m in Sources */ = {isa = PBXBuildFile; fileRef = F26F15B7268B6B5500EC54D3 /* iPadGestureTests.m */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -172,6 +173,7 @@
68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGestureRecognizerTests.m; sourceTree = "<group>"; };
68D4017B2564859300ECD91A /* ContinuousTexture.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ContinuousTexture.h; sourceTree = "<group>"; };
68D4017C2564859300ECD91A /* ContinuousTexture.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ContinuousTexture.m; sourceTree = "<group>"; };
F26F15B7268B6B5500EC54D3 /* iPadGestureTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = iPadGestureTests.m; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -295,6 +297,7 @@
246A6610252E693A00EAB0F3 /* RenderingSelectionTest.m */,
0DDEBC87258830B40065D0E8 /* SpawnEngineTest.h */,
0DDEBC88258830B40065D0E8 /* SpawnEngineTest.m */,
F26F15B7268B6B5500EC54D3 /* iPadGestureTests.m */,
);
path = ScenariosUITests;
sourceTree = "<group>";
Expand Down Expand Up @@ -490,6 +493,7 @@
6816DBA42318358200A51400 /* GoldenTestManager.m in Sources */,
248D76EF22E388380012F0C1 /* PlatformViewUITests.m in Sources */,
0D8470A4240F0B1F0030B565 /* StatusBarTest.m in Sources */,
F26F15B8268B6B5600EC54D3 /* iPadGestureTests.m in Sources */,
246A6611252E693A00EAB0F3 /* RenderingSelectionTest.m in Sources */,
4F06F1B32473296E000AF246 /* LocalizationInitializationTest.m in Sources */,
0A42BFB42447E179007E212E /* TextSemanticsFocusTest.m in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ - (BOOL)application:(UIApplication*)application
@"--platform-view-with-continuous-texture" : @"platform_view_with_continuous_texture",
@"--bogus-font-text" : @"bogus_font_text",
@"--spawn-engine-works" : @"spawn_engine_works",
@"--pointer-events" : @"pointer_events",
};
__block NSString* flutterViewControllerTestName = nil;
[launchArgsMap
Expand Down Expand Up @@ -109,6 +110,7 @@ - (void)setupFlutterViewControllerTest:(NSString*)scenarioIdentifier {
FlutterEngine* engine = [self engineForTest:scenarioIdentifier];
FlutterViewController* flutterViewController =
[self flutterViewControllerForTest:scenarioIdentifier withEngine:engine];
flutterViewController.view.accessibilityIdentifier = @"flutter_view";

[engine.binaryMessenger
setMessageHandlerOnChannel:@"waiting_for_status"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ - (void)testTapStatusBar {
[[self.application.statusBars firstMatch] tap];
}

XCUIElement* addTextField = self.application.textFields[@"PointerChange.add"];
XCUIElement* addTextField = self.application.textFields[@"PointerChange.add:0"];
BOOL exists = [addTextField waitForExistenceWithTimeout:1];
XCTAssertTrue(exists, @"");
XCUIElement* upTextField = self.application.textFields[@"PointerChange.up"];
XCUIElement* downTextField = self.application.textFields[@"PointerChange.down:0"];
exists = [downTextField waitForExistenceWithTimeout:1];
XCTAssertTrue(exists, @"");
XCUIElement* upTextField = self.application.textFields[@"PointerChange.up:0"];
exists = [upTextField waitForExistenceWithTimeout:1];
XCTAssertTrue(exists, @"");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright 2020 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import <XCTest/XCTest.h>

static const NSInteger kSecondsToWaitForFlutterView = 30;

@interface iPadGestureTests : XCTestCase

@end

@implementation iPadGestureTests

- (void)setUp {
[super setUp];
self.continueAfterFailure = NO;
}

#ifdef __IPHONE_15_0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you want conditional compilation here, you want runtime checks like you have below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this will compile on Xcode 12 without this conditional, since supportsPointerInteraction was only introduced in the Xcode 13 beta.

Copy link
Member

@gaaclarke gaaclarke Jul 2, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to do that use #if (defined (__IPHONE_15_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_15_0) then. We don't just care that the SDK has it, but that the target platform has it.

edit: nevermind, LGTM, sdk version is the same thing as running environment for tests.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment wouldn't hurt, a runtime check with respondsToSelector would be a bit more apparent and easier to maintain, but a comment will be good enough.

- (void)testPointerButtons {
if (@available(iOS 15, *)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How come this is asking for iOS 15 when the implementation check is for iOS 13.4?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the pointer/trackpad features on iPad came out in iPadOS 13.4, synthesizing those events in XCTest wasn't supported until this year's new Xcode/iPadOS beta releases.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You aren't synthesizing any events here though, right? At the least please add a comment, preferably one that points to the API that is being used that requires ios 15.

XCTSkipUnless([XCUIDevice.sharedDevice supportsPointerInteraction],
"Device does not support pointer interaction");
XCUIApplication* app = [[XCUIApplication alloc] init];
app.launchArguments = @[ @"--pointer-events" ];
[app launch];

NSPredicate* predicateToFindFlutterView =
[NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject,
NSDictionary<NSString*, id>* _Nullable bindings) {
XCUIElement* element = evaluatedObject;
return [element.identifier hasPrefix:@"flutter_view"];
}];
XCUIElement* flutterView = [[app descendantsMatchingType:XCUIElementTypeAny]
elementMatchingPredicate:predicateToFindFlutterView];
if (![flutterView waitForExistenceWithTimeout:kSecondsToWaitForFlutterView]) {
NSLog(@"%@", app.debugDescription);
XCTFail(@"Failed due to not able to find any flutterView with %@ seconds",
@(kSecondsToWaitForFlutterView));
}

XCTAssertNotNil(flutterView);

[flutterView tap];
// Initial add event should have buttons = 0
XCTAssertTrue([app.textFields[@"PointerChange.add:0"] waitForExistenceWithTimeout:1],
@"PointerChange.add event did not occur");
// Normal tap should have buttons = 0, the flutter framework will ensure it has buttons = 1
XCTAssertTrue([app.textFields[@"PointerChange.down:0"] waitForExistenceWithTimeout:1],
@"PointerChange.down event did not occur for a normal tap");
XCTAssertTrue([app.textFields[@"PointerChange.up:0"] waitForExistenceWithTimeout:1],
@"PointerChange.up event did not occur for a normal tap");
[flutterView rightClick];
// Since each touch is its own device, we can't distinguish the other add event(s)
// Right click should have buttons = 2
XCTAssertTrue([app.textFields[@"PointerChange.down:2"] waitForExistenceWithTimeout:1],
@"PointerChange.down event did not occur for a right-click");
XCTAssertTrue([app.textFields[@"PointerChange.up:2"] waitForExistenceWithTimeout:1],
@"PointerChange.up event did not occur for a right-click");
NSLog(@"DebugDescriptionX: %@", app.debugDescription);
}
}
#endif

@end
1 change: 1 addition & 0 deletions testing/scenario_app/lib/src/scenarios.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ Map<String, ScenarioFactory> _scenarios = <String, ScenarioFactory>{
'platform_view_with_continuous_texture': () => PlatformViewWithContinuousTexture(PlatformDispatcher.instance, 'Platform View', id: _viewId++),
'bogus_font_text': () => BogusFontText(PlatformDispatcher.instance),
'spawn_engine_works' : () => BogusFontText(PlatformDispatcher.instance),
'pointer_events': () => TouchesScenario(PlatformDispatcher.instance),
};

Map<String, dynamic> _currentScenarioParams = <String, dynamic>{};
Expand Down
24 changes: 17 additions & 7 deletions testing/scenario_app/lib/src/touches_scenario.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,24 @@ class TouchesScenario extends Scenario {
/// Constructor for `TouchesScenario`.
TouchesScenario(PlatformDispatcher dispatcher) : super(dispatcher);

@override
void onBeginFrame(Duration duration) {
// It is necessary to render frames for touch events to work properly on iOS
final Scene scene = SceneBuilder().build();
window.render(scene);
scene.dispose();
}

@override
void onPointerDataPacket(PointerDataPacket packet) {
sendJsonMessage(
dispatcher: dispatcher,
channel: 'display_data',
json: <String, dynamic>{
'data': packet.data[0].change.toString(),
},
);
for (final PointerData datum in packet.data) {
sendJsonMessage(
dispatcher: dispatcher,
channel: 'display_data',
json: <String, dynamic>{
'data': datum.change.toString() + ':' + datum.buttons.toString(),
},
);
}
}
}