From 6815c852821685f221f2f2e8d779aa503d9b4467 Mon Sep 17 00:00:00 2001 From: xster Date: Thu, 4 Apr 2019 16:22:37 -0700 Subject: [PATCH] Make sure FlutterViewController flushs all pending touches when no longer active (flutter/engine#8400) --- .../framework/Source/FlutterViewController.mm | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 076e3bec34..9bcbcff388 100644 --- a/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/engine/src/flutter/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -39,6 +39,7 @@ NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemantics BOOL _initialized; BOOL _viewOpaque; BOOL _engineNeedsLaunch; + NSMutableSet* _ongoingTouches; } #pragma mark - Manage and override all designated initializers @@ -54,6 +55,7 @@ NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemantics _engineNeedsLaunch = NO; _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); _weakFactory = std::make_unique>(self); + _ongoingTouches = [[NSMutableSet alloc] init]; [self performCommonViewControllerInitialization]; [engine setViewController:self]; @@ -75,6 +77,7 @@ NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemantics _flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]); [_engine.get() createShell:nil libraryURI:nil]; _engineNeedsLaunch = YES; + _ongoingTouches = [[NSMutableSet alloc] init]; [self loadDefaultSplashScreenView]; [self performCommonViewControllerInitialization]; } @@ -427,9 +430,44 @@ NSNotificationName const FlutterSemanticsUpdateNotification = @"FlutterSemantics 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. + 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)]; + } +} + - (void)dealloc { [_engine.get() notifyViewControllerDeallocated]; [[NSNotificationCenter defaultCenter] removeObserver:self]; @@ -528,6 +566,27 @@ static blink::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch) { 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 addObject:deviceKey]; + break; + case blink::PointerData::Change::kCancel: + case blink::PointerData::Change::kUp: + [_ongoingTouches removeObject: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.