From 53f0144c928c18b1346b995e3651e8b1876ff387 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 17 Jan 2020 15:04:10 -0800 Subject: [PATCH 01/14] draft1 --- .../ios/framework/Headers/FlutterPlugin.h | 53 +++++++++++++++++++ .../ios/framework/Source/FlutterEngine.mm | 12 ++++- .../framework/Source/FlutterPlatformViews.mm | 42 +++++++++++++-- .../Source/FlutterPlatformViews_Internal.h | 14 ++++- .../ios/Scenarios/Scenarios/AppDelegate.m | 22 ++++---- .../Scenarios/Scenarios/TextPlatformView.m | 53 ++++++++++++++++++- .../PlatformViewGestureRecognizerTests.m | 44 +++++++++++++++ testing/scenario_app/lib/main.dart | 1 + .../scenario_app/lib/src/platform_view.dart | 25 +++++++++ 9 files changed, 247 insertions(+), 19 deletions(-) create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h index 8092217fcfe35..80394d7e243b8 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -225,6 +225,42 @@ typedef void (*FlutterPluginRegistrantCallback)(NSObject* - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result; @end +#pragma mark - +/*************************************************************************************************** + * How the UIGestureRecognizers of a platform view should be blocked. + * + * UIGestureRecognizers of a platform views can be blocked based on the decisin made by the Flutter + * Framework. e.g. When an interactible widget is covering the platform view and a gesture happened + * on the widget, The framework may decide to block the UIGestureRecognizers that are recognized + * (via the gesture recognized acton ) on the platform view if any. + * + * This policy describes how the blocking process is implmented. + */ +typedef enum { + /** + * The flutter framwork blocks all the UIGestureRecognizers on the platform view as soon as it + * thinks they should be blocked. + * + * If this pociliy is implmented, only touchesBegan for all the UIGestureRecognizers is guaranteed + * to be called. + */ + FlutterPlatformViewGestureRecognizersBlockingPolicyEager, + /** + * The flutter framwork blocks all the UIGestureRecognizers until the `touchesEnded` method is + * called for every UIGestureRecognizers on the platform view. + * + * If this pociliy is implmented, all of the `touchesBegan`, `touchesMoved`, `touchesEnded` and + * `touchesCancelled` on any UIGestureRecognizers are guaranteed to be called if iOS system + * decided to call them. + */ + FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded, + /** + * The default behavior is currently set to `FlutterPlatformViewGestureBlockingPolicyEager` + */ + FlutterPlatformViewGestureRecognizersBlockingPolicyDefault = + FlutterPlatformViewGestureRecognizersBlockingPolicyEager, +} FlutterPlatformViewGestureRecognizersBlockingPolicy; + #pragma mark - /*************************************************************************************************** *Registration context for a single `FlutterPlugin`, providing a one stop shop @@ -264,6 +300,23 @@ typedef void (*FlutterPluginRegistrantCallback)(NSObject* - (void)registerViewFactory:(NSObject*)factory withId:(NSString*)factoryId; +/** + * Registers a `FlutterPlatformViewFactory` for creation of platform views. + * + * Plugins expose `UIView` for embedding in Flutter apps by registering a view factory. + * + * @param factory The view factory that will be registered. + * @param factoryId A unique identifier for the factory, the Dart code of the Flutter app can use + * this identifier to request creation of a `UIView` by the registered factory. + * @param gestureBlockingPolicy How UIGestureRecognizers on the platform view is + * blocked. + * + */ +- (void)registerViewFactory:(NSObject*)factory + withId:(NSString*)factoryId + gestureRecognizersBlockingPolicy: + (FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy; + /** * Publishes a value for external use of the plugin. * diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 4e9ea96d03de4..a6800c3e10cf7 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -737,7 +737,17 @@ - (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package { - (void)registerViewFactory:(NSObject*)factory withId:(NSString*)factoryId { - [_flutterEngine platformViewsController] -> RegisterViewFactory(factory, factoryId); + [self registerViewFactory:factory + withId:factoryId + gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyDefault]; +} + +- (void)registerViewFactory:(NSObject*)factory + withId:(NSString*)factoryId + gestureRecognizersBlockingPolicy: + (FlutterPlatformViewGestureRecognizersBlockingPolicy)gestureRecognizersBlockingPolicy { + [_flutterEngine platformViewsController] -> RegisterViewFactory(factory, factoryId, + gestureRecognizersBlockingPolicy); } @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index b4e4e4273602a..81e94abfb7138 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -86,8 +86,10 @@ views_[viewId] = fml::scoped_nsobject>([embedded_view retain]); FlutterTouchInterceptingView* touch_interceptor = [[[FlutterTouchInterceptingView alloc] - initWithEmbeddedView:embedded_view.view - flutterViewController:flutter_view_controller_.get()] autorelease]; + initWithEmbeddedView:embedded_view.view + flutterViewController:flutter_view_controller_.get() + gestureRecognizersBlockingPolicy:gesture_recognizers_blocking_policies[viewType]] + autorelease]; touch_interceptors_[viewId] = fml::scoped_nsobject([touch_interceptor retain]); @@ -149,11 +151,13 @@ void FlutterPlatformViewsController::RegisterViewFactory( NSObject* factory, - NSString* factoryId) { + NSString* factoryId, + FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy) { std::string idString([factoryId UTF8String]); FML_CHECK(factories_.count(idString) == 0); factories_[idString] = fml::scoped_nsobject>([factory retain]); + gesture_recognizers_blocking_policies[idString] = gestureRecognizerBlockingPolicy; } void FlutterPlatformViewsController::SetFrameSize(SkISize frame_size) { @@ -513,6 +517,9 @@ // invoking an acceptGesture method on the platform_views channel). And this is how we allow the // Flutter framework to delay or prevent the embedded view from getting a touch sequence. @interface DelayingGestureRecognizer : UIGestureRecognizer + +@property(nonatomic) bool shouldEndInNextTouchesEnded; + - (instancetype)initWithTarget:(id)target action:(SEL)action forwardingRecognizer:(UIGestureRecognizer*)forwardingRecognizer; @@ -535,9 +542,12 @@ - (instancetype)initWithTarget:(id)target @implementation FlutterTouchInterceptingView { fml::scoped_nsobject _delayingRecognizer; + FlutterPlatformViewGestureRecognizersBlockingPolicy _blockingPolicy; } - (instancetype)initWithEmbeddedView:(UIView*)embeddedView - flutterViewController:(UIViewController*)flutterViewController { + flutterViewController:(UIViewController*)flutterViewController + gestureRecognizersBlockingPolicy: + (FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy { self = [super initWithFrame:embeddedView.frame]; if (self) { self.multipleTouchEnabled = YES; @@ -554,6 +564,7 @@ - (instancetype)initWithEmbeddedView:(UIView*)embeddedView initWithTarget:self action:nil forwardingRecognizer:forwardingRecognizer]); + _blockingPolicy = blockingPolicy; [self addGestureRecognizer:_delayingRecognizer.get()]; [self addGestureRecognizer:forwardingRecognizer]; @@ -562,11 +573,22 @@ - (instancetype)initWithEmbeddedView:(UIView*)embeddedView } - (void)releaseGesture { + NSLog(@"releaseGesture gesture"); _delayingRecognizer.get().state = UIGestureRecognizerStateFailed; } - (void)blockGesture { - _delayingRecognizer.get().state = UIGestureRecognizerStateEnded; + switch (_blockingPolicy) { + case FlutterPlatformViewGestureRecognizersBlockingPolicyEager: + NSLog(@"block gesture"); + _delayingRecognizer.get().state = UIGestureRecognizerStateEnded; + break; + case FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded: + _delayingRecognizer.get().shouldEndInNextTouchesEnded = YES; + break; + default: + break; + } } // We want the intercepting view to consume the touches and not pass the touches up to the parent @@ -596,7 +618,9 @@ - (instancetype)initWithTarget:(id)target self = [super initWithTarget:target action:action]; if (self) { self.delaysTouchesBegan = YES; + self.delaysTouchesEnded = YES; self.delegate = self; + self.shouldEndInNextTouchesEnded = NO; _forwardingRecognizer.reset([forwardingRecognizer retain]); } return self; @@ -614,6 +638,14 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer return otherGestureRecognizer == self; } +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { + if (self.shouldEndInNextTouchesEnded) { + NSLog(@"block gesture in touches ended"); + self.state = UIGestureRecognizerStateEnded; + self.shouldEndInNextTouchesEnded = NO; + } +} + - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { self.state = UIGestureRecognizerStateFailed; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h index c8daeaa605946..bdd013c98e07f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h @@ -11,6 +11,7 @@ #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #include "flutter/shell/platform/darwin/common/framework/Headers/FlutterChannels.h" #include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlatformViews.h" +#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h" // A UIView that is used as the parent for embedded UIViews. // @@ -19,7 +20,9 @@ // 2. Dispatching all events that are hittested to the embedded view to the FlutterView. @interface FlutterTouchInterceptingView : UIView - (instancetype)initWithEmbeddedView:(UIView*)embeddedView - flutterViewController:(UIViewController*)flutterViewController; + flutterViewController:(UIViewController*)flutterViewController + gestureRecognizersBlockingPolicy: + (FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy; // Stop delaying any active touch sequence (and let it arrive the embedded view). - (void)releaseGesture; @@ -80,7 +83,10 @@ class FlutterPlatformViewsController { void SetFlutterViewController(UIViewController* flutter_view_controller); - void RegisterViewFactory(NSObject* factory, NSString* factoryId); + void RegisterViewFactory( + NSObject* factory, + NSString* factoryId, + FlutterPlatformViewGestureRecognizersBlockingPolicy gestureRecognizerBlockingPolicy); void SetFrameSize(SkISize frame_size); @@ -152,6 +158,10 @@ class FlutterPlatformViewsController { // Only compoiste platform views in this set. std::unordered_set views_to_recomposite_; + // The FlutterPlatformViewGestureRecognizersBlockingPolicy for each type of platform view. + std::map + gesture_recognizers_blocking_policies; + std::map> picture_recorders_; void OnCreate(FlutterMethodCall* call, FlutterResult& result); diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 86a601cc19495..87c4023d33725 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -47,19 +47,23 @@ - (BOOL)application:(UIApplication*)application } }]; - if (goldenTestName) { - [self readyContextForPlatformViewTests:goldenTestName]; - } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--screen-before-flutter"]) { - self.window.rootViewController = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:nil]; - } else { - self.window.rootViewController = [[UIViewController alloc] init]; - } +// if (goldenTestName) { +// [self readyContextForPlatformViewTests:goldenTestName gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyDefault]; +// } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--block-until-touches-ended"]) { +// [self readyContextForPlatformViewTests:@"platform_view_covered_by_widget" gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded]; +// } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--screen-before-flutter"]) { +// self.window.rootViewController = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:nil]; +// } else { +// self.window.rootViewController = [[UIViewController alloc] init]; +// } + + [self readyContextForPlatformViewTests:@"platform_view_covered_by_widget" gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyDefault]; [self.window makeKeyAndVisible]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } -- (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier { +- (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier gestureRecognizersBlockingPolicy:(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy{ FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"PlatformViewTest" project:nil]; [engine runWithEntrypoint:nil]; @@ -76,7 +80,7 @@ - (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier { [[TextPlatformViewFactory alloc] initWithMessenger:flutterViewController.binaryMessenger]; NSObject* registrar = [flutterViewController.engine registrarForPlugin:@"scenarios/TextPlatformViewPlugin"]; - [registrar registerViewFactory:textPlatformViewFactory withId:@"scenarios/textPlatformView"]; + [registrar registerViewFactory:textPlatformViewFactory withId:@"scenarios/textPlatformView" gestureRecognizersBlockingPolicy:blockingPolicy]; self.window.rootViewController = flutterViewController; } diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m index c75b5c815d0d6..513d97fb262a9 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m @@ -4,6 +4,35 @@ #import "TextPlatformView.h" +@protocol CustomGestureRecognizerDelegate; + +@interface CustomGestureRecognizer: UITapGestureRecognizer + +@property (weak, nonatomic) NSObject *customGestureRecognizerDelegate; + +@end + +@protocol CustomGestureRecognizerDelegate + +- (void)gestureRecognizer:(CustomGestureRecognizer *)gestureRecognizer touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; +- (void)gestureRecognizer:(CustomGestureRecognizer *)gestureRecognizer touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; + +@end + +@implementation CustomGestureRecognizer + +- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + [self.customGestureRecognizerDelegate gestureRecognizer:self touchesBegan:touches withEvent:event]; + [super touchesBegan:touches withEvent:event]; +} + +- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + [self.customGestureRecognizerDelegate gestureRecognizer:self touchesEnded:touches withEvent:event]; + [super touchesEnded:touches withEvent:event]; +} + +@end + @implementation TextPlatformViewFactory { NSObject* _messenger; } @@ -32,6 +61,10 @@ - (instancetype)initWithMessenger:(NSObject*)messenger { @end +@interface TextPlatformView() + +@end + @implementation TextPlatformView { int64_t _viewId; UITextView* _textView; @@ -47,8 +80,12 @@ - (instancetype)initWithFrame:(CGRect)frame _textView = [[UITextView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; _textView.textColor = UIColor.blueColor; _textView.backgroundColor = UIColor.lightGrayColor; + _textView.userInteractionEnabled = YES; [_textView setFont:[UIFont systemFontOfSize:52]]; _textView.text = args; + CustomGestureRecognizer *gestureRecognizer = [[CustomGestureRecognizer alloc] initWithTarget:self action:@selector(gestureDidRecognize:)]; + gestureRecognizer.customGestureRecognizerDelegate = self; + [_textView addGestureRecognizer:gestureRecognizer]; } return self; } @@ -57,7 +94,19 @@ - (UIView*)view { return _textView; } -// - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { -// } +- (void)gestureDidRecognize:(UIGestureRecognizer *)gestureRecognizer { + // An easy way to communicate with XCUITest target + self.accessibilityLabel = [self.accessibilityLabel stringByAppendingString:@"-gestureRecognized-"]; +} + +- (void)gestureRecognizer:(CustomGestureRecognizer *)gestureRecognizer touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { + // An easy way to communicate with XCUITest target + self.accessibilityLabel = [self.accessibilityLabel stringByAppendingString:@"-touchesBegan-"]; +} + +- (void)gestureRecognizer:(CustomGestureRecognizer *)gestureRecognizer touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { + // An easy way to communicate with XCUITest target + self.accessibilityLabel = [self.accessibilityLabel stringByAppendingString:@"-touchesEnded-"]; +} @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m new file mode 100644 index 0000000000000..b6337b761b54b --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m @@ -0,0 +1,44 @@ +// Copyright 2013 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 + +static const NSInteger kSecondsToWaitForPlatformView = 30; + +@interface PlatformViewGestureRecognizerTests : XCTestCase + +@property(nonatomic, strong) XCUIApplication* application; + + +@end + +@implementation PlatformViewGestureRecognizerTests + +- (void)setUp { + + self.continueAfterFailure = NO; + + self.application = [[XCUIApplication alloc] init]; + self.application.launchArguments = @[ @"--block-until-touches-ended" ]; + [self.application launch]; +} + +- (void)testExample { + + XCUIElement* element = self.application.textViews.firstMatch; + BOOL exists = [element waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]; + if (!exists) { + XCTFail(@"It took longer than %@ second to find the platform view." + @"There might be issues with the platform view's construction," + @"or with how the scenario is built.", + @(kSecondsToWaitForPlatformView)); + } + CGRect rect = element.frame; + XCUICoordinate *coordinate = [self.application coordinateWithNormalizedOffset:CGVectorMake(rect.origin.x+10, rect.origin.y+10)]; + [coordinate tap]; + + NSLog(@"accessibility label %@", element.accessibilityLabel); +} + +@end diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index f387bf4668baf..beb32974ee083 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -26,6 +26,7 @@ Map _scenarios = { 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), 'platform_view_rotate': PlatformViewScenario(window, 'Rotate Platform View', id: 10), + 'platform_view_covered_by_widget': PlatformViewCoveredByWidgetScenario(window, 'PlatformViewCovered', id:6) }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 05efe52fa8b25..44d6e97653321 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -281,6 +281,31 @@ class PlatformViewOpacityScenario extends PlatformViewScenario { } } +/// Platform view covered entirely by a widget. +class PlatformViewCoveredByWidgetScenario extends PlatformViewScenario { + /// Constructs a platform view covered entirely by a widget scenario + PlatformViewCoveredByWidgetScenario(Window window, String text, {int id = 0}) + : super(window, text, id: id); + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder sceneBuilder = SceneBuilder(); + sceneBuilder.pushOffset(0, 0); + _addPlatformViewtoScene(sceneBuilder, 6, 500, 500); + // final PictureRecorder recorder = PictureRecorder(); + // final Canvas canvas = Canvas(recorder); + // canvas.drawRect( + // const Rect.fromLTWH(0, 0, 500, 500), + // Paint()..color = const Color(0xFFABCDEF), + // ); + // final Picture picture = recorder.endRecording(); + // sceneBuilder.addPicture(const Offset(0, 0), picture); + final Scene scene = sceneBuilder.build(); + window.render(scene); + scene.dispose(); + } +} + mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; From cda277d38eb60874fcd7c7247368cb6c8f596bc3 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 23 Jan 2020 10:38:51 -0800 Subject: [PATCH 02/14] remove not working testing code --- .../ios/Scenarios/Scenarios/AppDelegate.m | 22 ++++----- .../Scenarios/Scenarios/TextPlatformView.m | 49 +------------------ .../PlatformViewGestureRecognizerTests.m | 44 ----------------- testing/scenario_app/lib/main.dart | 1 - .../scenario_app/lib/src/platform_view.dart | 25 ---------- 5 files changed, 10 insertions(+), 131 deletions(-) delete mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 87c4023d33725..86a601cc19495 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -47,23 +47,19 @@ - (BOOL)application:(UIApplication*)application } }]; -// if (goldenTestName) { -// [self readyContextForPlatformViewTests:goldenTestName gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyDefault]; -// } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--block-until-touches-ended"]) { -// [self readyContextForPlatformViewTests:@"platform_view_covered_by_widget" gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded]; -// } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--screen-before-flutter"]) { -// self.window.rootViewController = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:nil]; -// } else { -// self.window.rootViewController = [[UIViewController alloc] init]; -// } + if (goldenTestName) { + [self readyContextForPlatformViewTests:goldenTestName]; + } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--screen-before-flutter"]) { + self.window.rootViewController = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:nil]; + } else { + self.window.rootViewController = [[UIViewController alloc] init]; + } - - [self readyContextForPlatformViewTests:@"platform_view_covered_by_widget" gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyDefault]; [self.window makeKeyAndVisible]; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } -- (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier gestureRecognizersBlockingPolicy:(FlutterPlatformViewGestureRecognizersBlockingPolicy)blockingPolicy{ +- (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier { FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"PlatformViewTest" project:nil]; [engine runWithEntrypoint:nil]; @@ -80,7 +76,7 @@ - (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier gestureRe [[TextPlatformViewFactory alloc] initWithMessenger:flutterViewController.binaryMessenger]; NSObject* registrar = [flutterViewController.engine registrarForPlugin:@"scenarios/TextPlatformViewPlugin"]; - [registrar registerViewFactory:textPlatformViewFactory withId:@"scenarios/textPlatformView" gestureRecognizersBlockingPolicy:blockingPolicy]; + [registrar registerViewFactory:textPlatformViewFactory withId:@"scenarios/textPlatformView"]; self.window.rootViewController = flutterViewController; } diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m index 513d97fb262a9..427848564a2eb 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m @@ -4,35 +4,6 @@ #import "TextPlatformView.h" -@protocol CustomGestureRecognizerDelegate; - -@interface CustomGestureRecognizer: UITapGestureRecognizer - -@property (weak, nonatomic) NSObject *customGestureRecognizerDelegate; - -@end - -@protocol CustomGestureRecognizerDelegate - -- (void)gestureRecognizer:(CustomGestureRecognizer *)gestureRecognizer touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; -- (void)gestureRecognizer:(CustomGestureRecognizer *)gestureRecognizer touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; - -@end - -@implementation CustomGestureRecognizer - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - [self.customGestureRecognizerDelegate gestureRecognizer:self touchesBegan:touches withEvent:event]; - [super touchesBegan:touches withEvent:event]; -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - [self.customGestureRecognizerDelegate gestureRecognizer:self touchesEnded:touches withEvent:event]; - [super touchesEnded:touches withEvent:event]; -} - -@end - @implementation TextPlatformViewFactory { NSObject* _messenger; } @@ -61,7 +32,7 @@ - (instancetype)initWithMessenger:(NSObject*)messenger { @end -@interface TextPlatformView() +@interface TextPlatformView() @end @@ -83,9 +54,6 @@ - (instancetype)initWithFrame:(CGRect)frame _textView.userInteractionEnabled = YES; [_textView setFont:[UIFont systemFontOfSize:52]]; _textView.text = args; - CustomGestureRecognizer *gestureRecognizer = [[CustomGestureRecognizer alloc] initWithTarget:self action:@selector(gestureDidRecognize:)]; - gestureRecognizer.customGestureRecognizerDelegate = self; - [_textView addGestureRecognizer:gestureRecognizer]; } return self; } @@ -94,19 +62,4 @@ - (UIView*)view { return _textView; } -- (void)gestureDidRecognize:(UIGestureRecognizer *)gestureRecognizer { - // An easy way to communicate with XCUITest target - self.accessibilityLabel = [self.accessibilityLabel stringByAppendingString:@"-gestureRecognized-"]; -} - -- (void)gestureRecognizer:(CustomGestureRecognizer *)gestureRecognizer touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { - // An easy way to communicate with XCUITest target - self.accessibilityLabel = [self.accessibilityLabel stringByAppendingString:@"-touchesBegan-"]; -} - -- (void)gestureRecognizer:(CustomGestureRecognizer *)gestureRecognizer touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event { - // An easy way to communicate with XCUITest target - self.accessibilityLabel = [self.accessibilityLabel stringByAppendingString:@"-touchesEnded-"]; -} - @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m deleted file mode 100644 index b6337b761b54b..0000000000000 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2013 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 - -static const NSInteger kSecondsToWaitForPlatformView = 30; - -@interface PlatformViewGestureRecognizerTests : XCTestCase - -@property(nonatomic, strong) XCUIApplication* application; - - -@end - -@implementation PlatformViewGestureRecognizerTests - -- (void)setUp { - - self.continueAfterFailure = NO; - - self.application = [[XCUIApplication alloc] init]; - self.application.launchArguments = @[ @"--block-until-touches-ended" ]; - [self.application launch]; -} - -- (void)testExample { - - XCUIElement* element = self.application.textViews.firstMatch; - BOOL exists = [element waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]; - if (!exists) { - XCTFail(@"It took longer than %@ second to find the platform view." - @"There might be issues with the platform view's construction," - @"or with how the scenario is built.", - @(kSecondsToWaitForPlatformView)); - } - CGRect rect = element.frame; - XCUICoordinate *coordinate = [self.application coordinateWithNormalizedOffset:CGVectorMake(rect.origin.x+10, rect.origin.y+10)]; - [coordinate tap]; - - NSLog(@"accessibility label %@", element.accessibilityLabel); -} - -@end diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index beb32974ee083..f387bf4668baf 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -26,7 +26,6 @@ Map _scenarios = { 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), 'platform_view_rotate': PlatformViewScenario(window, 'Rotate Platform View', id: 10), - 'platform_view_covered_by_widget': PlatformViewCoveredByWidgetScenario(window, 'PlatformViewCovered', id:6) }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 44d6e97653321..05efe52fa8b25 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -281,31 +281,6 @@ class PlatformViewOpacityScenario extends PlatformViewScenario { } } -/// Platform view covered entirely by a widget. -class PlatformViewCoveredByWidgetScenario extends PlatformViewScenario { - /// Constructs a platform view covered entirely by a widget scenario - PlatformViewCoveredByWidgetScenario(Window window, String text, {int id = 0}) - : super(window, text, id: id); - - @override - void onBeginFrame(Duration duration) { - final SceneBuilder sceneBuilder = SceneBuilder(); - sceneBuilder.pushOffset(0, 0); - _addPlatformViewtoScene(sceneBuilder, 6, 500, 500); - // final PictureRecorder recorder = PictureRecorder(); - // final Canvas canvas = Canvas(recorder); - // canvas.drawRect( - // const Rect.fromLTWH(0, 0, 500, 500), - // Paint()..color = const Color(0xFFABCDEF), - // ); - // final Picture picture = recorder.endRecording(); - // sceneBuilder.addPicture(const Offset(0, 0), picture); - final Scene scene = sceneBuilder.build(); - window.render(scene); - scene.dispose(); - } -} - mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; From c251e27d340b58af8b2951ab4bf444ee5f927b8f Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 23 Jan 2020 10:50:12 -0800 Subject: [PATCH 03/14] formatting --- .../framework/Source/FlutterPlatformViews.mm | 22 ++++++++++++++++++- .../Scenarios/Scenarios/TextPlatformView.m | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 81e94abfb7138..640bbed4b1567 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -519,6 +519,7 @@ @interface DelayingGestureRecognizer : UIGestureRecognizer @property(nonatomic) bool shouldEndInNextTouchesEnded; +@property(nonatomic) bool touchedEnded; - (instancetype)initWithTarget:(id)target action:(SEL)action @@ -584,7 +585,17 @@ - (void)blockGesture { _delayingRecognizer.get().state = UIGestureRecognizerStateEnded; break; case FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded: - _delayingRecognizer.get().shouldEndInNextTouchesEnded = YES; + if (_delayingRecognizer.get().touchedEnded) { + // If touchesEnded of the `DelayingGesureRecognizer` has been already invoked, + // we want to set the state of the `DelayingGesureRecognizer` to + // `UIGestureRecognizerStateEnded` as soon as possible. + _delayingRecognizer.get().state = UIGestureRecognizerStateEnded; + } else { + // If touchesEnded of the `DelayingGesureRecognizer` has not been invoked, + // We will set a flag to notify the `DelayingGesureRecognizer` to set the state to + // `UIGestureRecognizerStateEnded` when touchesEnded is called. + _delayingRecognizer.get().shouldEndInNextTouchesEnded = YES; + } break; default: break; @@ -621,6 +632,7 @@ - (instancetype)initWithTarget:(id)target self.delaysTouchesEnded = YES; self.delegate = self; self.shouldEndInNextTouchesEnded = NO; + self.touchedEnded = NO; _forwardingRecognizer.reset([forwardingRecognizer retain]); } return self; @@ -638,12 +650,20 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer return otherGestureRecognizer == self; } +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { + self.touchedEnded = NO; + [super touchesBegan:touches withEvent:event]; +} + - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { if (self.shouldEndInNextTouchesEnded) { NSLog(@"block gesture in touches ended"); self.state = UIGestureRecognizerStateEnded; self.shouldEndInNextTouchesEnded = NO; + return; } + self.touchedEnded = YES; + [super touchesEnded:touches withEvent:event]; } - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m index 427848564a2eb..8a57cf5f58f53 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m @@ -32,7 +32,7 @@ - (instancetype)initWithMessenger:(NSObject*)messenger { @end -@interface TextPlatformView() +@interface TextPlatformView () @end From e9e8e909fb7ac1795cd1d9a27c643a4ec128d840 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 23 Jan 2020 10:57:47 -0800 Subject: [PATCH 04/14] renaming and commenting --- .../ios/framework/Source/FlutterPlatformViews.mm | 16 +++++++++++----- .../ios/Scenarios/Scenarios/TextPlatformView.m | 4 ---- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index 640bbed4b1567..d84b1a30a0c66 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -518,8 +518,13 @@ // Flutter framework to delay or prevent the embedded view from getting a touch sequence. @interface DelayingGestureRecognizer : UIGestureRecognizer +// Indicates that if the `DelayingGestureRecognizer`'s state should be set to +// `UIGestureRecognizerStateEnded` during next `touchesEnded` call. @property(nonatomic) bool shouldEndInNextTouchesEnded; -@property(nonatomic) bool touchedEnded; + +// Indicates that the `DelayingGestureRecognizer`'s `touchesEnded` has been invoked without +// setting the state to `UIGestureRecognizerStateEnded`. +@property(nonatomic) bool touchedEndedWithoutBlocking; - (instancetype)initWithTarget:(id)target action:(SEL)action @@ -582,10 +587,11 @@ - (void)blockGesture { switch (_blockingPolicy) { case FlutterPlatformViewGestureRecognizersBlockingPolicyEager: NSLog(@"block gesture"); + // We block all other gesture recognizers immediately in this policy. _delayingRecognizer.get().state = UIGestureRecognizerStateEnded; break; case FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded: - if (_delayingRecognizer.get().touchedEnded) { + if (_delayingRecognizer.get().touchedEndedWithoutBlocking) { // If touchesEnded of the `DelayingGesureRecognizer` has been already invoked, // we want to set the state of the `DelayingGesureRecognizer` to // `UIGestureRecognizerStateEnded` as soon as possible. @@ -632,7 +638,7 @@ - (instancetype)initWithTarget:(id)target self.delaysTouchesEnded = YES; self.delegate = self; self.shouldEndInNextTouchesEnded = NO; - self.touchedEnded = NO; + self.touchedEndedWithoutBlocking = NO; _forwardingRecognizer.reset([forwardingRecognizer retain]); } return self; @@ -651,7 +657,7 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer } - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { - self.touchedEnded = NO; + self.touchedEndedWithoutBlocking = NO; [super touchesBegan:touches withEvent:event]; } @@ -662,7 +668,7 @@ - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { self.shouldEndInNextTouchesEnded = NO; return; } - self.touchedEnded = YES; + self.touchedEndedWithoutBlocking = YES; [super touchesEnded:touches withEvent:event]; } diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m index 8a57cf5f58f53..f7d17d9c56feb 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m @@ -32,10 +32,6 @@ - (instancetype)initWithMessenger:(NSObject*)messenger { @end -@interface TextPlatformView () - -@end - @implementation TextPlatformView { int64_t _viewId; UITextView* _textView; From c6bd1986d225a7d880522dc5754d241c89cbce35 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 24 Jan 2020 13:35:17 -0800 Subject: [PATCH 05/14] review fixes --- .../ios/framework/Headers/FlutterPlugin.h | 22 +++++++++---------- .../framework/Source/FlutterPlatformViews.mm | 7 ++---- .../Scenarios/Scenarios/TextPlatformView.m | 1 - 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h index 80394d7e243b8..7e6e5cdeb2e6f 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -229,27 +229,27 @@ typedef void (*FlutterPluginRegistrantCallback)(NSObject* /*************************************************************************************************** * How the UIGestureRecognizers of a platform view should be blocked. * - * UIGestureRecognizers of a platform views can be blocked based on the decisin made by the Flutter - * Framework. e.g. When an interactible widget is covering the platform view and a gesture happened - * on the widget, The framework may decide to block the UIGestureRecognizers that are recognized - * (via the gesture recognized acton ) on the platform view if any. + * UIGestureRecognizers of a platform views can be blocked based on the decisions made by the + * Flutter Framework. e.g. When an interact-able widget is covering the platform view and a gesture + * happened on the widget, The framework may decide to block the UIGestureRecognizers that are + * recognized (via the gesture recognized acton ) on the platform view if any. * - * This policy describes how the blocking process is implmented. + * This policy describes how the blocking process is implemented. */ typedef enum { /** - * The flutter framwork blocks all the UIGestureRecognizers on the platform view as soon as it - * thinks they should be blocked. + * The flutter framework blocks all the UIGestureRecognizers on the platform view as soon as it + * decides they should be blocked. * - * If this pociliy is implmented, only touchesBegan for all the UIGestureRecognizers is guaranteed + * If this policy is implemented, only touchesBegan for all the UIGestureRecognizers is guaranteed * to be called. */ FlutterPlatformViewGestureRecognizersBlockingPolicyEager, /** - * The flutter framwork blocks all the UIGestureRecognizers until the `touchesEnded` method is + * The flutter framework blocks all the UIGestureRecognizers until the `touchesEnded` method is * called for every UIGestureRecognizers on the platform view. * - * If this pociliy is implmented, all of the `touchesBegan`, `touchesMoved`, `touchesEnded` and + * If this policy is implemented, all of the `touchesBegan`, `touchesMoved`, `touchesEnded` and * `touchesCancelled` on any UIGestureRecognizers are guaranteed to be called if iOS system * decided to call them. */ @@ -308,7 +308,7 @@ typedef enum { * @param factory The view factory that will be registered. * @param factoryId A unique identifier for the factory, the Dart code of the Flutter app can use * this identifier to request creation of a `UIView` by the registered factory. - * @param gestureBlockingPolicy How UIGestureRecognizers on the platform view is + * @param gestureBlockingPolicy How UIGestureRecognizers on the platform views are * blocked. * */ diff --git a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm index d84b1a30a0c66..e7a30d61add75 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews.mm @@ -579,14 +579,12 @@ - (instancetype)initWithEmbeddedView:(UIView*)embeddedView } - (void)releaseGesture { - NSLog(@"releaseGesture gesture"); _delayingRecognizer.get().state = UIGestureRecognizerStateFailed; } - (void)blockGesture { switch (_blockingPolicy) { case FlutterPlatformViewGestureRecognizersBlockingPolicyEager: - NSLog(@"block gesture"); // We block all other gesture recognizers immediately in this policy. _delayingRecognizer.get().state = UIGestureRecognizerStateEnded; break; @@ -663,12 +661,11 @@ - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { if (self.shouldEndInNextTouchesEnded) { - NSLog(@"block gesture in touches ended"); self.state = UIGestureRecognizerStateEnded; self.shouldEndInNextTouchesEnded = NO; - return; + } else { + self.touchedEndedWithoutBlocking = YES; } - self.touchedEndedWithoutBlocking = YES; [super touchesEnded:touches withEvent:event]; } diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m index f7d17d9c56feb..786f01e95c290 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m @@ -47,7 +47,6 @@ - (instancetype)initWithFrame:(CGRect)frame _textView = [[UITextView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; _textView.textColor = UIColor.blueColor; _textView.backgroundColor = UIColor.lightGrayColor; - _textView.userInteractionEnabled = YES; [_textView setFont:[UIFont systemFontOfSize:52]]; _textView.text = args; } From 9cf53c59f245746680fd874ac08e9d1323e9ab40 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 6 Feb 2020 15:23:26 -0800 Subject: [PATCH 06/14] add first test --- .../plugins/GeneratedPluginRegistrant.java | 23 +++++ .../ios/Runner/GeneratedPluginRegistrant.h | 17 ++++ .../ios/Runner/GeneratedPluginRegistrant.m | 12 +++ .../ios/Scenarios/Scenarios/AppDelegate.m | 20 +++- .../Scenarios/Scenarios/TextPlatformView.m | 59 +++++++++++- .../PlatformViewGestureRecognizerTests.m | 66 ++++++++++++++ testing/scenario_app/lib/main.dart | 1 + .../scenario_app/lib/src/platform_view.dart | 91 ++++++++++++++++--- 8 files changed, 270 insertions(+), 19 deletions(-) create mode 100644 testing/scenario_app/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java create mode 100644 testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.h create mode 100644 testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.m create mode 100644 testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m diff --git a/testing/scenario_app/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java b/testing/scenario_app/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java new file mode 100644 index 0000000000000..d007606a44d83 --- /dev/null +++ b/testing/scenario_app/android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java @@ -0,0 +1,23 @@ +package io.flutter.plugins; + +import io.flutter.plugin.common.PluginRegistry; + +/** + * Generated file. Do not edit. + */ +public final class GeneratedPluginRegistrant { + public static void registerWith(PluginRegistry registry) { + if (alreadyRegisteredWith(registry)) { + return; + } + } + + private static boolean alreadyRegisteredWith(PluginRegistry registry) { + final String key = GeneratedPluginRegistrant.class.getCanonicalName(); + if (registry.hasPlugin(key)) { + return true; + } + registry.registrarFor(key); + return false; + } +} diff --git a/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.h b/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.h new file mode 100644 index 0000000000000..ed9a5c61691e5 --- /dev/null +++ b/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.h @@ -0,0 +1,17 @@ +// +// Generated file. Do not edit. +// + +#ifndef GeneratedPluginRegistrant_h +#define GeneratedPluginRegistrant_h + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface GeneratedPluginRegistrant : NSObject ++ (void)registerWithRegistry:(NSObject*)registry; +@end + +NS_ASSUME_NONNULL_END +#endif /* GeneratedPluginRegistrant_h */ diff --git a/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.m b/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.m new file mode 100644 index 0000000000000..60dfa42b328db --- /dev/null +++ b/testing/scenario_app/ios/Runner/GeneratedPluginRegistrant.m @@ -0,0 +1,12 @@ +// +// Generated file. Do not edit. +// + +#import "GeneratedPluginRegistrant.h" + +@implementation GeneratedPluginRegistrant + ++ (void)registerWithRegistry:(NSObject*)registry { +} + +@end diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 86a601cc19495..0c56d4755e890 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -48,7 +48,17 @@ - (BOOL)application:(UIApplication*)application }]; if (goldenTestName) { - [self readyContextForPlatformViewTests:goldenTestName]; + [self + readyContextForPlatformViewTests:goldenTestName + gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager]; + } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--gesture"]) { + FlutterPlatformViewGestureRecognizersBlockingPolicy policy = + FlutterPlatformViewGestureRecognizersBlockingPolicyEager; + if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--until-touches-ended"]) { + policy = FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded; + } + [self readyContextForPlatformViewTests:@"platform_view_touch" + gestureRecognizersBlockingPolicy:policy]; } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--screen-before-flutter"]) { self.window.rootViewController = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:nil]; } else { @@ -59,7 +69,9 @@ - (BOOL)application:(UIApplication*)application return [super application:application didFinishLaunchingWithOptions:launchOptions]; } -- (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier { +- (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier + gestureRecognizersBlockingPolicy: + (FlutterPlatformViewGestureRecognizersBlockingPolicy)policy { FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"PlatformViewTest" project:nil]; [engine runWithEntrypoint:nil]; @@ -76,7 +88,9 @@ - (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier { [[TextPlatformViewFactory alloc] initWithMessenger:flutterViewController.binaryMessenger]; NSObject* registrar = [flutterViewController.engine registrarForPlugin:@"scenarios/TextPlatformViewPlugin"]; - [registrar registerViewFactory:textPlatformViewFactory withId:@"scenarios/textPlatformView"]; + [registrar registerViewFactory:textPlatformViewFactory + withId:@"scenarios/textPlatformView" + gestureRecognizersBlockingPolicy:policy]; self.window.rootViewController = flutterViewController; } diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m index 786f01e95c290..76e3e460b800a 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m @@ -4,6 +4,36 @@ #import "TextPlatformView.h" +@protocol TestGestureRecognizerDelegate + +- (void)gestureTouchesBegan; +- (void)gestureTouchesEnded; + +@end + +@interface TestTapGestureRecognizer : UITapGestureRecognizer + +@property(assign, nonatomic) BOOL touchesBegan; +@property(assign, nonatomic) BOOL touchesEnd; +@property(weak, nonatomic) + NSObject* testTapGestureRecognizerDelegate; + +@end + +@implementation TestTapGestureRecognizer + +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { + [self.testTapGestureRecognizerDelegate gestureTouchesBegan]; + [super touchesBegan:touches withEvent:event]; +} + +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { + [self.testTapGestureRecognizerDelegate gestureTouchesEnded]; + [super touchesEnded:touches withEvent:event]; +} + +@end + @implementation TextPlatformViewFactory { NSObject* _messenger; } @@ -32,8 +62,11 @@ - (instancetype)initWithMessenger:(NSObject*)messenger { @end +@interface TextPlatformView () + +@end + @implementation TextPlatformView { - int64_t _viewId; UITextView* _textView; FlutterMethodChannel* _channel; } @@ -43,12 +76,19 @@ - (instancetype)initWithFrame:(CGRect)frame arguments:(id _Nullable)args binaryMessenger:(NSObject*)messenger { if ([super init]) { - _viewId = viewId; _textView = [[UITextView alloc] initWithFrame:CGRectMake(50.0, 50.0, 250.0, 100.0)]; _textView.textColor = UIColor.blueColor; _textView.backgroundColor = UIColor.lightGrayColor; [_textView setFont:[UIFont systemFontOfSize:52]]; _textView.text = args; + _textView.accessibilityIdentifier = @"platform_view"; + + TestTapGestureRecognizer* gestureRecognizer = + [[TestTapGestureRecognizer alloc] initWithTarget:self action:@selector(platformViewTapped)]; + + [_textView addGestureRecognizer:gestureRecognizer]; + gestureRecognizer.testTapGestureRecognizerDelegate = self; + _textView.accessibilityLabel = @""; } return self; } @@ -57,4 +97,19 @@ - (UIView*)view { return _textView; } +- (void)platformViewTapped { + _textView.accessibilityLabel = + [_textView.accessibilityLabel stringByAppendingString:@"-platformViewTapped"]; +} + +- (void)gestureTouchesBegan { + _textView.accessibilityLabel = + [_textView.accessibilityLabel stringByAppendingString:@"-gestureTouchesBegan"]; +} + +- (void)gestureTouchesEnded { + _textView.accessibilityLabel = + [_textView.accessibilityLabel stringByAppendingString:@"-gestureTouchesEnded"]; +} + @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m new file mode 100644 index 0000000000000..e93381f6b9e51 --- /dev/null +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m @@ -0,0 +1,66 @@ +// Copyright 2013 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 + +static const NSInteger kSecondsToWaitForPlatformView = 30; + +@interface PlatformViewGestureRecognizerTests : XCTestCase + +@end + +@implementation PlatformViewGestureRecognizerTests + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the + // class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + self.continueAfterFailure = NO; + + // In UI tests it’s important to set the initial state - such as interface orientation - required + // for your tests before they run. The setUp method is a good place to do this. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the + // class. +} + +- (void)testExample { + // UI tests must launch the application that they test. + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--gesture", @"--accept", @"--until-touches-ended" ]; + [app launch]; + + NSPredicate* predicateToFindPlatformView = + [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, + NSDictionary* _Nullable bindings) { + XCUIElement* element = evaluatedObject; + return [element.identifier isEqualToString:@"platform_view"]; + }]; + XCUIElement* platformView = + [app.otherElements elementMatchingPredicate:predicateToFindPlatformView]; + if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { + NSLog(@"%@", app.debugDescription); + XCTFail(@"Failed due to not able to find any platformView with %@ seconds", + @(kSecondsToWaitForPlatformView)); + } + + XCTAssertNotNil(platformView); + XCTAssertEqualObjects(platformView.label, @""); + + NSPredicate* predicate = [NSPredicate + predicateWithFormat:@"label == %@", + @"-gestureTouchesBegan-gestureTouchesEnded-platformViewTapped"]; + XCTNSPredicateExpectation* expection = + [[XCTNSPredicateExpectation alloc] initWithPredicate:predicate object:platformView]; + + [platformView tap]; + [self waitForExpectations:@[ expection ] timeout:kSecondsToWaitForPlatformView]; + XCTAssertEqualObjects(platformView.label, + @"-gestureTouchesBegan-gestureTouchesEnded-platformViewTapped"); +} + +@end diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 2f0a7688702f2..87e8a18609d4c 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -26,6 +26,7 @@ Map _scenarios = { 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), 'platform_view_rotate': PlatformViewScenario(window, 'Rotate Platform View', id: 10), + 'platform_view_touch': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11), }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 05efe52fa8b25..75d5b23254b04 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -27,7 +27,8 @@ List _to64(num value) { } /// A simple platform view. -class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { +class PlatformViewScenario extends Scenario + with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// /// The [window] parameter must not be null. @@ -48,7 +49,8 @@ class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin } /// Builds a scene with 2 platform views. -class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { +class MultiPlatformViewScenario extends Scenario + with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// /// The [window] parameter must not be null. @@ -84,11 +86,13 @@ class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioM /// Renders a frame with 2 platform views covered by a flutter drawn rectangle, /// when the app goes to the background and comes back to the foreground renders a new frame /// with the 2 platform views but without the flutter drawn rectangle. -class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BasePlatformViewScenarioMixin { +class MultiPlatformViewBackgroundForegroundScenario extends Scenario + with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// /// The [window] parameter must not be null. - MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) + MultiPlatformViewBackgroundForegroundScenario(Window window, + {this.firstId, this.secondId}) : assert(window != null), super(window) { createPlatformView(window, 'platform view 1', firstId); @@ -154,15 +158,16 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP @override void onPlatformMessage( - String name, - ByteData data, - PlatformMessageResponseCallback callback, - ) { + String name, + ByteData data, + PlatformMessageResponseCallback callback, + ) { if (name != 'flutter/lifecycle') { return; } final String message = utf8.decode(data.buffer.asUint8List()); - if (_lastLifecycleState == 'AppLifecycleState.inactive' && message == 'AppLifecycleState.resumed') { + if (_lastLifecycleState == 'AppLifecycleState.inactive' && + message == 'AppLifecycleState.resumed') { _nextFrame = _secondFrame; window.scheduleFrame(); } @@ -172,7 +177,8 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BaseP } /// Platform view with clip rect. -class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenarioMixin { +class PlatformViewClipRectScenario extends Scenario + with _BasePlatformViewScenarioMixin { /// Constructs a platform view with clip rect scenario. PlatformViewClipRectScenario(Window window, String text, {int id = 0}) : assert(window != null), @@ -281,6 +287,58 @@ class PlatformViewOpacityScenario extends PlatformViewScenario { } } +/// A simple platform view for testing touch events from iOS. +class PlatformViewForTouchIOSScenario extends Scenario + with _BasePlatformViewScenarioMixin { + + int _viewId; + /// Creates the PlatformView scenario. + /// + /// The [window] parameter must not be null. + PlatformViewForTouchIOSScenario(Window window, String text, {int id = 0}) + : assert(window != null), + super(window) { + createPlatformView(window, text, id); + _viewId = id; + } + + @override + void onBeginFrame(Duration duration) { + final SceneBuilder builder = SceneBuilder(); + + builder.pushOffset(0, 0); + finishBuilderByAddingPlatformViewAndPicture(builder, 11); + } + + @override + void onPointerDataPacket(PointerDataPacket packet) { + if (packet.data.first.change == PointerChange.add) { + const String method = 'acceptGesture'; + const int _valueString = 7; + const int _valueInt32 = 3; + const int _valueMap = 13; + final Uint8List message = Uint8List.fromList([ + _valueString, + method.length, + ...utf8.encode(method), + _valueMap, + 1, + _valueString, + 'id'.length, + ...utf8.encode('id'), + _valueInt32, + ..._to32(_viewId), + ]); + window.sendPlatformMessage( + 'flutter/platform_views', + message.buffer.asByteData(), + (ByteData response) {}, + ); + } + + } +} + mixin _BasePlatformViewScenarioMixin on Scenario { int _textureId; @@ -290,6 +348,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. void createPlatformView(Window window, String text, int id) { + print('create $id'); const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; @@ -351,18 +410,22 @@ mixin _BasePlatformViewScenarioMixin on Scenario { ); } - void _addPlatformViewtoScene(SceneBuilder sceneBuilder, int viewId, double width, double height) { + void _addPlatformViewtoScene( + SceneBuilder sceneBuilder, int viewId, double width, double height) { if (Platform.isIOS) { sceneBuilder.addPlatformView(viewId, width: width, height: height); } else if (Platform.isAndroid && _textureId != null) { - sceneBuilder.addTexture(_textureId, offset: const Offset(150, 300), width: width, height: height); + sceneBuilder.addTexture(_textureId, + offset: const Offset(150, 300), width: width, height: height); } else { - throw UnsupportedError('Platform ${Platform.operatingSystem} is not supported'); + throw UnsupportedError( + 'Platform ${Platform.operatingSystem} is not supported'); } } // Add a platform view and a picture to the scene, then finish the `sceneBuilder`. - void finishBuilderByAddingPlatformViewAndPicture(SceneBuilder sceneBuilder, int viewId) { + void finishBuilderByAddingPlatformViewAndPicture( + SceneBuilder sceneBuilder, int viewId) { _addPlatformViewtoScene(sceneBuilder, viewId, 500, 500); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); From 7b06bb3a2ba04524125438595dc7b7254ef798be Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 6 Feb 2020 15:23:37 -0800 Subject: [PATCH 07/14] add test file to xcode target --- .../ios/Scenarios/Scenarios.xcodeproj/project.pbxproj | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj index fd490352a2256..5da90fd32a674 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj +++ b/testing/scenario_app/ios/Scenarios/Scenarios.xcodeproj/project.pbxproj @@ -50,6 +50,7 @@ 6816DBAC2318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */; }; 6816DBAD2318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */; }; 6816DBAE2318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png in Resources */ = {isa = PBXBuildFile; fileRef = 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */; }; + 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -156,6 +157,7 @@ 6816DBA72318696600A51400 /* golden_platform_view_opacity_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_opacity_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA82318696600A51400 /* golden_platform_view_cliprect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprect_iPhone SE_simulator.png"; sourceTree = ""; }; 6816DBA92318696600A51400 /* golden_platform_view_cliprrect_iPhone SE_simulator.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "golden_platform_view_cliprrect_iPhone SE_simulator.png"; sourceTree = ""; }; + 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PlatformViewGestureRecognizerTests.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -269,6 +271,7 @@ 6816DBA02317573300A51400 /* GoldenImage.m */, 6816DBA22318358200A51400 /* PlatformViewGoldenTestManager.h */, 6816DBA32318358200A51400 /* PlatformViewGoldenTestManager.m */, + 68A5B63323EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m */, ); path = ScenariosUITests; sourceTree = ""; @@ -458,6 +461,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 68A5B63423EB71D300BDBCDB /* PlatformViewGestureRecognizerTests.m in Sources */, 6816DBA12317573300A51400 /* GoldenImage.m in Sources */, 6816DB9E231750ED00A51400 /* GoldenPlatformViewTests.m in Sources */, 6816DBA42318358200A51400 /* PlatformViewGoldenTestManager.m in Sources */, From ef6f47039e55a2542af76afbf39239f7e26b60e8 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Thu, 6 Feb 2020 16:15:53 -0800 Subject: [PATCH 08/14] add tests for accept scenario --- .../ios/framework/Headers/FlutterPlugin.h | 20 ++--- .../ios/framework/Source/FlutterEngine.mm | 2 +- .../ios/Scenarios/Scenarios/AppDelegate.m | 8 +- .../PlatformViewGestureRecognizerTests.m | 80 +++++++++++++++---- testing/scenario_app/lib/main.dart | 3 +- .../scenario_app/lib/src/platform_view.dart | 12 ++- 6 files changed, 90 insertions(+), 35 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h index 7e6e5cdeb2e6f..a150708fccc50 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -234,31 +234,25 @@ typedef void (*FlutterPluginRegistrantCallback)(NSObject* * happened on the widget, The framework may decide to block the UIGestureRecognizers that are * recognized (via the gesture recognized acton ) on the platform view if any. * - * This policy describes how the blocking process is implemented. + * This policy determines how Flutter blocks a platform view's UIGestureRecognizers. */ typedef enum { /** - * The flutter framework blocks all the UIGestureRecognizers on the platform view as soon as it + * Flutter blocks all the UIGestureRecognizers on the platform view as soon as it * decides they should be blocked. * - * If this policy is implemented, only touchesBegan for all the UIGestureRecognizers is guaranteed + * With this policy, only touchesBegan for all the UIGestureRecognizers is guaranteed * to be called. */ FlutterPlatformViewGestureRecognizersBlockingPolicyEager, /** - * The flutter framework blocks all the UIGestureRecognizers until the `touchesEnded` method is - * called for every UIGestureRecognizers on the platform view. + * Flutter blocks the platform view's UIGestureRecognizers from recognizing only after + * touchesEnded was invoked. * - * If this policy is implemented, all of the `touchesBegan`, `touchesMoved`, `touchesEnded` and - * `touchesCancelled` on any UIGestureRecognizers are guaranteed to be called if iOS system - * decided to call them. + * This results in the platform view's UIGestureRecognizers seeing the entire touch sequence, + * but never recognizing the gesture (and never invoking actions). */ FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded, - /** - * The default behavior is currently set to `FlutterPlatformViewGestureBlockingPolicyEager` - */ - FlutterPlatformViewGestureRecognizersBlockingPolicyDefault = - FlutterPlatformViewGestureRecognizersBlockingPolicyEager, } FlutterPlatformViewGestureRecognizersBlockingPolicy; #pragma mark - diff --git a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm index 84ddc93af2e01..7a8034156864f 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterEngine.mm @@ -743,7 +743,7 @@ - (void)registerViewFactory:(NSObject*)factory withId:(NSString*)factoryId { [self registerViewFactory:factory withId:factoryId - gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyDefault]; + gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager]; } - (void)registerViewFactory:(NSObject*)factory diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 0c56d4755e890..21e934820b874 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -51,14 +51,18 @@ - (BOOL)application:(UIApplication*)application [self readyContextForPlatformViewTests:goldenTestName gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager]; - } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--gesture"]) { + } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--gesture-reject"]) { FlutterPlatformViewGestureRecognizersBlockingPolicy policy = FlutterPlatformViewGestureRecognizersBlockingPolicyEager; if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--until-touches-ended"]) { policy = FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded; } - [self readyContextForPlatformViewTests:@"platform_view_touch" + [self readyContextForPlatformViewTests:@"platform_view_touch_reject" gestureRecognizersBlockingPolicy:policy]; + } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--gesture-accept"]) { + [self readyContextForPlatformViewTests:@"platform_view_touch_accept" + gestureRecognizersBlockingPolicy: + FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded]; } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--screen-before-flutter"]) { self.window.rootViewController = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:nil]; } else { diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m index e93381f6b9e51..69b45b5231871 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m @@ -13,25 +13,78 @@ @interface PlatformViewGestureRecognizerTests : XCTestCase @implementation PlatformViewGestureRecognizerTests - (void)setUp { - // Put setup code here. This method is called before the invocation of each test method in the - // class. - - // In UI tests it is usually best to stop immediately when a failure occurs. self.continueAfterFailure = NO; +} + +- (void)testRejcectPolicyUtilTouchesEnded { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--gesture-reject", @"--until-touches-ended" ]; + [app launch]; + + NSPredicate* predicateToFindPlatformView = + [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, + NSDictionary* _Nullable bindings) { + XCUIElement* element = evaluatedObject; + return [element.identifier isEqualToString:@"platform_view"]; + }]; + XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; + if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { + NSLog(@"%@", app.debugDescription); + XCTFail(@"Failed due to not able to find any platformView with %@ seconds", + @(kSecondsToWaitForPlatformView)); + } + + XCTAssertNotNil(platformView); + XCTAssertEqualObjects(platformView.label, @""); - // In UI tests it’s important to set the initial state - such as interface orientation - required - // for your tests before they run. The setUp method is a good place to do this. + NSPredicate* predicate = + [NSPredicate predicateWithFormat:@"label == %@", @"-gestureTouchesBegan-gestureTouchesEnded"]; + XCTNSPredicateExpectation* expection = + [[XCTNSPredicateExpectation alloc] initWithPredicate:predicate object:platformView]; + + [platformView tap]; + [self waitForExpectations:@[ expection ] timeout:kSecondsToWaitForPlatformView]; + XCTAssertEqualObjects(platformView.label, @"-gestureTouchesBegan-gestureTouchesEnded"); } -- (void)tearDown { - // Put teardown code here. This method is called after the invocation of each test method in the - // class. +- (void)testRejcectPolicyEager { + XCUIApplication* app = [[XCUIApplication alloc] init]; + app.launchArguments = @[ @"--gesture-reject" ]; + [app launch]; + + NSPredicate* predicateToFindPlatformView = + [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, + NSDictionary* _Nullable bindings) { + XCUIElement* element = evaluatedObject; + return [element.identifier isEqualToString:@"platform_view"]; + }]; + XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; + if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { + NSLog(@"%@", app.debugDescription); + XCTFail(@"Failed due to not able to find any platformView with %@ seconds", + @(kSecondsToWaitForPlatformView)); + } + + XCTAssertNotNil(platformView); + XCTAssertEqualObjects(platformView.label, @""); + + NSPredicate* predicate = + [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, + NSDictionary* _Nullable bindings) { + XCUIElement* view = (XCUIElement*)evaluatedObject; + return [view.label containsString:@"-gestureTouchesBegan"]; + }]; + XCTNSPredicateExpectation* expection = + [[XCTNSPredicateExpectation alloc] initWithPredicate:predicate object:platformView]; + + [platformView tap]; + [self waitForExpectations:@[ expection ] timeout:kSecondsToWaitForPlatformView]; + XCTAssertTrue([platformView.label containsString:@"-gestureTouchesBegan"]); } -- (void)testExample { - // UI tests must launch the application that they test. +- (void)testAccept { XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--gesture", @"--accept", @"--until-touches-ended" ]; + app.launchArguments = @[ @"--gesture-accept" ]; [app launch]; NSPredicate* predicateToFindPlatformView = @@ -40,8 +93,7 @@ - (void)testExample { XCUIElement* element = evaluatedObject; return [element.identifier isEqualToString:@"platform_view"]; }]; - XCUIElement* platformView = - [app.otherElements elementMatchingPredicate:predicateToFindPlatformView]; + XCUIElement* platformView = [app.textViews elementMatchingPredicate:predicateToFindPlatformView]; if (![platformView waitForExistenceWithTimeout:kSecondsToWaitForPlatformView]) { NSLog(@"%@", app.debugDescription); XCTFail(@"Failed due to not able to find any platformView with %@ seconds", diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 87e8a18609d4c..646731777ec04 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -26,7 +26,8 @@ Map _scenarios = { 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), 'platform_view_rotate': PlatformViewScenario(window, 'Rotate Platform View', id: 10), - 'platform_view_touch': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11), + 'platform_view_touch_reject': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11, accept: false), + 'platform_view_touch_accept': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11, accept: true), }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 75d5b23254b04..5f7c128f771cf 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -292,14 +292,16 @@ class PlatformViewForTouchIOSScenario extends Scenario with _BasePlatformViewScenarioMixin { int _viewId; + bool _accept; /// Creates the PlatformView scenario. /// /// The [window] parameter must not be null. - PlatformViewForTouchIOSScenario(Window window, String text, {int id = 0}) + PlatformViewForTouchIOSScenario(Window window, String text, {int id = 0, bool accept}) : assert(window != null), + _accept = accept, + _viewId = id, super(window) { createPlatformView(window, text, id); - _viewId = id; } @override @@ -313,7 +315,10 @@ class PlatformViewForTouchIOSScenario extends Scenario @override void onPointerDataPacket(PointerDataPacket packet) { if (packet.data.first.change == PointerChange.add) { - const String method = 'acceptGesture'; + String method = 'rejectGesture'; + if (_accept) { + method = 'acceptGesture'; + } const int _valueString = 7; const int _valueInt32 = 3; const int _valueMap = 13; @@ -348,7 +353,6 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. void createPlatformView(Window window, String text, int id) { - print('create $id'); const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; From 839c3f71c0116b7f0214bcc099301fd2aef4bc3d Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 7 Feb 2020 11:32:57 -0800 Subject: [PATCH 09/14] update comments --- .../darwin/ios/framework/Headers/FlutterPlugin.h | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h index a150708fccc50..eed8f6ff5490d 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -227,21 +227,17 @@ typedef void (*FlutterPluginRegistrantCallback)(NSObject* #pragma mark - /*************************************************************************************************** - * How the UIGestureRecognizers of a platform view should be blocked. + * How the UIGestureRecognizers of a platform view are blocked. * - * UIGestureRecognizers of a platform views can be blocked based on the decisions made by the - * Flutter Framework. e.g. When an interact-able widget is covering the platform view and a gesture - * happened on the widget, The framework may decide to block the UIGestureRecognizers that are - * recognized (via the gesture recognized acton ) on the platform view if any. - * - * This policy determines how Flutter blocks a platform view's UIGestureRecognizers. + * UIGestureRecognizers of platform views can be blocked based on decisions made by the + * Flutter Framework (e.g. When an interact-able widget is covering the platform view). */ typedef enum { /** * Flutter blocks all the UIGestureRecognizers on the platform view as soon as it * decides they should be blocked. * - * With this policy, only touchesBegan for all the UIGestureRecognizers is guaranteed + * With this policy, only the `touchesBegan` method for all the UIGestureRecognizers is guaranteed * to be called. */ FlutterPlatformViewGestureRecognizersBlockingPolicyEager, From 705a1054d13dcdb382c7e38ebf0e0319acf450ca Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 7 Feb 2020 13:10:46 -0800 Subject: [PATCH 10/14] revert format on platform_view.dart --- .../scenario_app/lib/src/platform_view.dart | 38 +++++++------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index 5f7c128f771cf..e9f913d1a99a6 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -27,8 +27,7 @@ List _to64(num value) { } /// A simple platform view. -class PlatformViewScenario extends Scenario - with _BasePlatformViewScenarioMixin { +class PlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// /// The [window] parameter must not be null. @@ -49,8 +48,7 @@ class PlatformViewScenario extends Scenario } /// Builds a scene with 2 platform views. -class MultiPlatformViewScenario extends Scenario - with _BasePlatformViewScenarioMixin { +class MultiPlatformViewScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// /// The [window] parameter must not be null. @@ -86,13 +84,11 @@ class MultiPlatformViewScenario extends Scenario /// Renders a frame with 2 platform views covered by a flutter drawn rectangle, /// when the app goes to the background and comes back to the foreground renders a new frame /// with the 2 platform views but without the flutter drawn rectangle. -class MultiPlatformViewBackgroundForegroundScenario extends Scenario - with _BasePlatformViewScenarioMixin { +class MultiPlatformViewBackgroundForegroundScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Creates the PlatformView scenario. /// /// The [window] parameter must not be null. - MultiPlatformViewBackgroundForegroundScenario(Window window, - {this.firstId, this.secondId}) + MultiPlatformViewBackgroundForegroundScenario(Window window, {this.firstId, this.secondId}) : assert(window != null), super(window) { createPlatformView(window, 'platform view 1', firstId); @@ -158,16 +154,15 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario @override void onPlatformMessage( - String name, - ByteData data, - PlatformMessageResponseCallback callback, - ) { + String name, + ByteData data, + PlatformMessageResponseCallback callback, + ) { if (name != 'flutter/lifecycle') { return; } final String message = utf8.decode(data.buffer.asUint8List()); - if (_lastLifecycleState == 'AppLifecycleState.inactive' && - message == 'AppLifecycleState.resumed') { + if (_lastLifecycleState == 'AppLifecycleState.inactive' && message == 'AppLifecycleState.resumed') { _nextFrame = _secondFrame; window.scheduleFrame(); } @@ -177,8 +172,7 @@ class MultiPlatformViewBackgroundForegroundScenario extends Scenario } /// Platform view with clip rect. -class PlatformViewClipRectScenario extends Scenario - with _BasePlatformViewScenarioMixin { +class PlatformViewClipRectScenario extends Scenario with _BasePlatformViewScenarioMixin { /// Constructs a platform view with clip rect scenario. PlatformViewClipRectScenario(Window window, String text, {int id = 0}) : assert(window != null), @@ -414,22 +408,18 @@ mixin _BasePlatformViewScenarioMixin on Scenario { ); } - void _addPlatformViewtoScene( - SceneBuilder sceneBuilder, int viewId, double width, double height) { + void _addPlatformViewtoScene(SceneBuilder sceneBuilder, int viewId, double width, double height) { if (Platform.isIOS) { sceneBuilder.addPlatformView(viewId, width: width, height: height); } else if (Platform.isAndroid && _textureId != null) { - sceneBuilder.addTexture(_textureId, - offset: const Offset(150, 300), width: width, height: height); + sceneBuilder.addTexture(_textureId, offset: const Offset(150, 300), width: width, height: height); } else { - throw UnsupportedError( - 'Platform ${Platform.operatingSystem} is not supported'); + throw UnsupportedError('Platform ${Platform.operatingSystem} is not supported'); } } // Add a platform view and a picture to the scene, then finish the `sceneBuilder`. - void finishBuilderByAddingPlatformViewAndPicture( - SceneBuilder sceneBuilder, int viewId) { + void finishBuilderByAddingPlatformViewAndPicture(SceneBuilder sceneBuilder, int viewId) { _addPlatformViewtoScene(sceneBuilder, viewId, 500, 500); final PictureRecorder recorder = PictureRecorder(); final Canvas canvas = Canvas(recorder); From 4df622afa9c95cc8428846ec2db887f9b5cf3583 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 7 Feb 2020 13:15:41 -0800 Subject: [PATCH 11/14] typo --- .../scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m | 5 +---- .../ScenariosUITests/PlatformViewGestureRecognizerTests.m | 4 ++-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m index 76e3e460b800a..fedca96aa7695 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m @@ -13,10 +13,7 @@ - (void)gestureTouchesEnded; @interface TestTapGestureRecognizer : UITapGestureRecognizer -@property(assign, nonatomic) BOOL touchesBegan; -@property(assign, nonatomic) BOOL touchesEnd; -@property(weak, nonatomic) - NSObject* testTapGestureRecognizerDelegate; +@property(weak, nonatomic) NSObject* testTapGestureRecognizerDelegate; @end diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m index 69b45b5231871..c3efdf68c0711 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m @@ -16,7 +16,7 @@ - (void)setUp { self.continueAfterFailure = NO; } -- (void)testRejcectPolicyUtilTouchesEnded { +- (void)testRejectPolicyUtilTouchesEnded { XCUIApplication* app = [[XCUIApplication alloc] init]; app.launchArguments = @[ @"--gesture-reject", @"--until-touches-ended" ]; [app launch]; @@ -47,7 +47,7 @@ - (void)testRejcectPolicyUtilTouchesEnded { XCTAssertEqualObjects(platformView.label, @"-gestureTouchesBegan-gestureTouchesEnded"); } -- (void)testRejcectPolicyEager { +- (void)testRejectPolicyEager { XCUIApplication* app = [[XCUIApplication alloc] init]; app.launchArguments = @[ @"--gesture-reject" ]; [app launch]; From f262ca073eb86ef7dbda927b60853a7bfd54368f Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 7 Feb 2020 13:15:55 -0800 Subject: [PATCH 12/14] formatting --- .../scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m index fedca96aa7695..e01bca6c61e67 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/TextPlatformView.m @@ -13,7 +13,8 @@ - (void)gestureTouchesEnded; @interface TestTapGestureRecognizer : UITapGestureRecognizer -@property(weak, nonatomic) NSObject* testTapGestureRecognizerDelegate; +@property(weak, nonatomic) + NSObject* testTapGestureRecognizerDelegate; @end From 488f394b4bf15de4d15f32264e740e6624f42364 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 7 Feb 2020 13:42:16 -0800 Subject: [PATCH 13/14] review fixes --- .../ios/Scenarios/Scenarios/AppDelegate.m | 38 ++++++++----------- .../PlatformViewGestureRecognizerTests.m | 4 +- testing/scenario_app/lib/main.dart | 5 ++- .../scenario_app/lib/src/platform_view.dart | 14 ++++--- 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m index 21e934820b874..8805ad80bd644 100644 --- a/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m +++ b/testing/scenario_app/ios/Scenarios/Scenarios/AppDelegate.m @@ -25,7 +25,8 @@ - (BOOL)application:(UIApplication*)application // This argument is used by the XCUITest for Platform Views so that the app // under test will create platform views. - // The launchArgsMap should match the one in the `PlatformVieGoldenTestManager`. + // If the test is one of the platform view golden tests, + // the launchArgsMap should match the one in the `PlatformVieGoldenTestManager`. NSDictionary* launchArgsMap = @{ @"--platform-view" : @"platform_view", @"--platform-view-multiple" : @"platform_view_multiple", @@ -37,32 +38,21 @@ - (BOOL)application:(UIApplication*)application @"--platform-view-transform" : @"platform_view_transform", @"--platform-view-opacity" : @"platform_view_opacity", @"--platform-view-rotate" : @"platform_view_rotate", + @"--gesture-reject-after-touches-ended" : @"platform_view_gesture_reject_after_touches_ended", + @"--gesture-reject-eager" : @"platform_view_gesture_reject_eager", + @"--gesture-accept" : @"platform_view_gesture_accept", }; - __block NSString* goldenTestName = nil; + __block NSString* platformViewTestName = nil; [launchArgsMap enumerateKeysAndObjectsUsingBlock:^(NSString* argument, NSString* testName, BOOL* stop) { if ([[[NSProcessInfo processInfo] arguments] containsObject:argument]) { - goldenTestName = testName; + platformViewTestName = testName; *stop = YES; } }]; - if (goldenTestName) { - [self - readyContextForPlatformViewTests:goldenTestName - gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager]; - } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--gesture-reject"]) { - FlutterPlatformViewGestureRecognizersBlockingPolicy policy = - FlutterPlatformViewGestureRecognizersBlockingPolicyEager; - if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--until-touches-ended"]) { - policy = FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded; - } - [self readyContextForPlatformViewTests:@"platform_view_touch_reject" - gestureRecognizersBlockingPolicy:policy]; - } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--gesture-accept"]) { - [self readyContextForPlatformViewTests:@"platform_view_touch_accept" - gestureRecognizersBlockingPolicy: - FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded]; + if (platformViewTestName) { + [self readyContextForPlatformViewTests:platformViewTestName]; } else if ([[[NSProcessInfo processInfo] arguments] containsObject:@"--screen-before-flutter"]) { self.window.rootViewController = [[ScreenBeforeFlutter alloc] initWithEngineRunCompletion:nil]; } else { @@ -73,9 +63,7 @@ - (BOOL)application:(UIApplication*)application return [super application:application didFinishLaunchingWithOptions:launchOptions]; } -- (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier - gestureRecognizersBlockingPolicy: - (FlutterPlatformViewGestureRecognizersBlockingPolicy)policy { +- (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier { FlutterEngine* engine = [[FlutterEngine alloc] initWithName:@"PlatformViewTest" project:nil]; [engine runWithEntrypoint:nil]; @@ -94,7 +82,11 @@ - (void)readyContextForPlatformViewTests:(NSString*)scenarioIdentifier [flutterViewController.engine registrarForPlugin:@"scenarios/TextPlatformViewPlugin"]; [registrar registerViewFactory:textPlatformViewFactory withId:@"scenarios/textPlatformView" - gestureRecognizersBlockingPolicy:policy]; + gestureRecognizersBlockingPolicy:FlutterPlatformViewGestureRecognizersBlockingPolicyEager]; + [registrar registerViewFactory:textPlatformViewFactory + withId:@"scenarios/textPlatformView_blockPolicyUntilTouchesEnded" + gestureRecognizersBlockingPolicy: + FlutterPlatformViewGestureRecognizersBlockingPolicyWaitUntilTouchesEnded]; self.window.rootViewController = flutterViewController; } diff --git a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m index c3efdf68c0711..d791210f22707 100644 --- a/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m +++ b/testing/scenario_app/ios/Scenarios/ScenariosUITests/PlatformViewGestureRecognizerTests.m @@ -18,7 +18,7 @@ - (void)setUp { - (void)testRejectPolicyUtilTouchesEnded { XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--gesture-reject", @"--until-touches-ended" ]; + app.launchArguments = @[ @"--gesture-reject-after-touches-ended" ]; [app launch]; NSPredicate* predicateToFindPlatformView = @@ -49,7 +49,7 @@ - (void)testRejectPolicyUtilTouchesEnded { - (void)testRejectPolicyEager { XCUIApplication* app = [[XCUIApplication alloc] init]; - app.launchArguments = @[ @"--gesture-reject" ]; + app.launchArguments = @[ @"--gesture-reject-eager" ]; [app launch]; NSPredicate* predicateToFindPlatformView = diff --git a/testing/scenario_app/lib/main.dart b/testing/scenario_app/lib/main.dart index 646731777ec04..de647de5fb7c3 100644 --- a/testing/scenario_app/lib/main.dart +++ b/testing/scenario_app/lib/main.dart @@ -26,8 +26,9 @@ Map _scenarios = { 'platform_view_multiple_background_foreground': MultiPlatformViewBackgroundForegroundScenario(window, firstId: 8, secondId: 9), 'poppable_screen': PoppableScreenScenario(window), 'platform_view_rotate': PlatformViewScenario(window, 'Rotate Platform View', id: 10), - 'platform_view_touch_reject': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11, accept: false), - 'platform_view_touch_accept': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11, accept: true), + 'platform_view_gesture_reject_eager': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11, accept: false), + 'platform_view_gesture_accept': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11, accept: true), + 'platform_view_gesture_reject_after_touches_ended': PlatformViewForTouchIOSScenario(window, 'platform view touch', id: 11, accept: false, rejectUntilTouchesEnded: true), }; Scenario _currentScenario = _scenarios['animated_color_square']; diff --git a/testing/scenario_app/lib/src/platform_view.dart b/testing/scenario_app/lib/src/platform_view.dart index e9f913d1a99a6..6726cdf3f3ec7 100644 --- a/testing/scenario_app/lib/src/platform_view.dart +++ b/testing/scenario_app/lib/src/platform_view.dart @@ -290,12 +290,16 @@ class PlatformViewForTouchIOSScenario extends Scenario /// Creates the PlatformView scenario. /// /// The [window] parameter must not be null. - PlatformViewForTouchIOSScenario(Window window, String text, {int id = 0, bool accept}) + PlatformViewForTouchIOSScenario(Window window, String text, {int id = 0, bool accept, bool rejectUntilTouchesEnded = false}) : assert(window != null), _accept = accept, _viewId = id, super(window) { - createPlatformView(window, text, id); + if (rejectUntilTouchesEnded) { + createPlatformView(window, text, id, viewType: 'scenarios/textPlatformView_blockPolicyUntilTouchesEnded'); + } else { + createPlatformView(window, text, id); + } } @override @@ -346,7 +350,7 @@ mixin _BasePlatformViewScenarioMixin on Scenario { /// It prepare a TextPlatformView so it can be added to the SceneBuilder in `onBeginFrame`. /// Call this method in the constructor of the platform view related scenarios /// to perform necessary set up. - void createPlatformView(Window window, String text, int id) { + void createPlatformView(Window window, String text, int id, {String viewType = 'scenarios/textPlatformView'}) { const int _valueInt32 = 3; const int _valueFloat64 = 6; const int _valueString = 7; @@ -370,8 +374,8 @@ mixin _BasePlatformViewScenarioMixin on Scenario { 'viewType'.length, ...utf8.encode('viewType'), _valueString, - 'scenarios/textPlatformView'.length, - ...utf8.encode('scenarios/textPlatformView'), + viewType.length, + ...utf8.encode(viewType), if (Platform.isAndroid) ...[ _valueString, 'width'.length, From 6f67a00921992253fc054617abf56b500dd67b62 Mon Sep 17 00:00:00 2001 From: Chris Yang Date: Fri, 7 Feb 2020 13:45:05 -0800 Subject: [PATCH 14/14] review fixes --- shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h index eed8f6ff5490d..287301dbc2dbc 100644 --- a/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h +++ b/shell/platform/darwin/ios/framework/Headers/FlutterPlugin.h @@ -293,7 +293,7 @@ typedef enum { /** * Registers a `FlutterPlatformViewFactory` for creation of platform views. * - * Plugins expose `UIView` for embedding in Flutter apps by registering a view factory. + * Plugins can expose a `UIView` for embedding in Flutter apps by registering a view factory. * * @param factory The view factory that will be registered. * @param factoryId A unique identifier for the factory, the Dart code of the Flutter app can use