From 2d9f681e76ba288f740f5054c52283a0d035dc9f Mon Sep 17 00:00:00 2001 From: Xiao Yu Date: Mon, 1 Apr 2019 16:53:18 -0700 Subject: [PATCH 1/3] Make sure FlutterViewController flushs all pending touches when no longer active --- .../framework/Source/FlutterViewController.mm | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 076e3bec3402b..6bfab71746b09 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -39,6 +39,7 @@ @implementation FlutterViewController { BOOL _initialized; BOOL _viewOpaque; BOOL _engineNeedsLaunch; + NSMutableDictionary* _ongoingTouches; } #pragma mark - Manage and override all designated initializers @@ -54,6 +55,7 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine _engineNeedsLaunch = NO; _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); _weakFactory = std::make_unique>(self); + _ongoingTouches = [[NSMutableDictionary alloc] init]; [self performCommonViewControllerInitialization]; [engine setViewController:self]; @@ -75,6 +77,7 @@ - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); [_engine.get() createShell:nil libraryURI:nil]; _engineNeedsLaunch = YES; + _ongoingTouches = [[NSMutableDictionary alloc] init]; [self loadDefaultSplashScreenView]; [self performCommonViewControllerInitialization]; } @@ -427,6 +430,15 @@ - (void)viewDidDisappear:(BOOL)animated { TRACE_EVENT0("flutter", "viewDidDisappear"); [self surfaceUpdated:NO]; [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.paused"]; + if (_ongoingTouches.count > 0) { + // If the view controller is going away, we want to flush cancel all the ongoing + // touches to the framework so nothing gets orphaned. + blink::PointerData::Change cancelChange = blink::PointerData::Change::kCancel; + [self dispatchTouches:[NSSet setWithArray:[_ongoingTouches allValues]] + pointerDataChangeOverride:&cancelChange]; + [_ongoingTouches removeAllObjects]; + } + [super viewDidDisappear:animated]; } @@ -528,6 +540,27 @@ - (void)dispatchTouches:(NSSet*)touches pointer_data.physical_x = windowCoordinates.x * scale; pointer_data.physical_y = windowCoordinates.y * scale; + NSNumber* deviceKey = [NSNumber numberWithLongLong:pointer_data.device]; + // Track touches that began and not yet stopped so we can flush them + // if the view controller goes away. + switch (pointer_data.change) { + case blink::PointerData::Change::kDown: + [_ongoingTouches setObject:touch forKey:deviceKey]; + break; + case blink::PointerData::Change::kCancel: + case blink::PointerData::Change::kUp: + [_ongoingTouches removeObjectForKey:deviceKey]; + break; + case blink::PointerData::Change::kHover: + case blink::PointerData::Change::kMove: + // We're only tracking starts and stops. + break; + case blink::PointerData::Change::kAdd: + case blink::PointerData::Change::kRemove: + // We don't use kAdd/kRemove. + break; + } + // pressure_min is always 0.0 if (@available(iOS 9, *)) { // These properties were introduced in iOS 9.0. From 8fb1fa5f861802a575e80c5d40aca217e4d8fc0d Mon Sep 17 00:00:00 2001 From: Xiao Yu Date: Wed, 3 Apr 2019 18:23:00 -0700 Subject: [PATCH 2/3] Make our own pointer data without UITouch --- .../framework/Source/FlutterViewController.mm | 46 +++++++++++++++---- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 6bfab71746b09..a0523c0e9c761 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -39,7 +39,7 @@ @implementation FlutterViewController { BOOL _initialized; BOOL _viewOpaque; BOOL _engineNeedsLaunch; - NSMutableDictionary* _ongoingTouches; + NSMutableSet* _ongoingTouches; } #pragma mark - Manage and override all designated initializers @@ -55,7 +55,7 @@ - (instancetype)initWithEngine:(FlutterEngine*)engine _engineNeedsLaunch = NO; _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); _weakFactory = std::make_unique>(self); - _ongoingTouches = [[NSMutableDictionary alloc] init]; + _ongoingTouches = [[NSMutableSet alloc] init]; [self performCommonViewControllerInitialization]; [engine setViewController:self]; @@ -77,7 +77,7 @@ - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); [_engine.get() createShell:nil libraryURI:nil]; _engineNeedsLaunch = YES; - _ongoingTouches = [[NSMutableDictionary alloc] init]; + _ongoingTouches = [[NSMutableSet alloc] init]; [self loadDefaultSplashScreenView]; [self performCommonViewControllerInitialization]; } @@ -430,16 +430,42 @@ - (void)viewDidDisappear:(BOOL)animated { TRACE_EVENT0("flutter", "viewDidDisappear"); [self surfaceUpdated:NO]; [[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.paused"]; + [self flushOngoingTouches]; + + [super viewDidDisappear:animated]; +} + +- (void)flushOngoingTouches { if (_ongoingTouches.count > 0) { + auto packet = std::make_unique(_ongoingTouches.count); + size_t pointer_index = 0; // If the view controller is going away, we want to flush cancel all the ongoing // touches to the framework so nothing gets orphaned. - blink::PointerData::Change cancelChange = blink::PointerData::Change::kCancel; - [self dispatchTouches:[NSSet setWithArray:[_ongoingTouches allValues]] - pointerDataChangeOverride:&cancelChange]; + for(NSNumber* device in _ongoingTouches) { + // Create fake PointerData to balance out each previously started one for the framework. + blink::PointerData pointer_data; + pointer_data.Clear(); + + constexpr int kMicrosecondsPerSecond = 1000 * 1000; + // Use current time. + pointer_data.time_stamp = [[NSDate date] timeIntervalSince1970] * kMicrosecondsPerSecond; + + pointer_data.change = blink::PointerData::Change::kCancel; + pointer_data.kind = blink::PointerData::DeviceKind::kTouch; + pointer_data.device = device.longLongValue; + + // Anything we put here will be arbitrary since there are no touches. + pointer_data.physical_x = 0; + pointer_data.physical_y = 0; + pointer_data.pressure = 1.0; + pointer_data.pressure_max = 1.0; + + packet->SetPointerData(pointer_index++, pointer_data); + } + [_ongoingTouches removeAllObjects]; + [_engine.get() dispatchPointerDataPacket:std::move(packet)]; } - - [super viewDidDisappear:animated]; } - (void)dealloc { @@ -545,11 +571,11 @@ - (void)dispatchTouches:(NSSet*)touches // if the view controller goes away. switch (pointer_data.change) { case blink::PointerData::Change::kDown: - [_ongoingTouches setObject:touch forKey:deviceKey]; + [_ongoingTouches addObject:deviceKey]; break; case blink::PointerData::Change::kCancel: case blink::PointerData::Change::kUp: - [_ongoingTouches removeObjectForKey:deviceKey]; + [_ongoingTouches removeObject:deviceKey]; break; case blink::PointerData::Change::kHover: case blink::PointerData::Change::kMove: From daf6c59fad29c5ae42f3395f7e5b858184453c09 Mon Sep 17 00:00:00 2001 From: Xiao Yu Date: Thu, 4 Apr 2019 15:09:40 -0700 Subject: [PATCH 3/3] add type --- .../darwin/ios/framework/Source/FlutterViewController.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index a0523c0e9c761..9bcbcff388a47 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -39,7 +39,7 @@ @implementation FlutterViewController { BOOL _initialized; BOOL _viewOpaque; BOOL _engineNeedsLaunch; - NSMutableSet* _ongoingTouches; + NSMutableSet* _ongoingTouches; } #pragma mark - Manage and override all designated initializers @@ -441,7 +441,7 @@ - (void)flushOngoingTouches { size_t pointer_index = 0; // If the view controller is going away, we want to flush cancel all the ongoing // touches to the framework so nothing gets orphaned. - for(NSNumber* device in _ongoingTouches) { + for (NSNumber* device in _ongoingTouches) { // Create fake PointerData to balance out each previously started one for the framework. blink::PointerData pointer_data; pointer_data.Clear();