diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 2fb9f90c6f281..b64d2f7089928 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -21,8 +21,13 @@ #include "flutter/shell/platform/darwin/ios/framework/Source/platform_message_response_darwin.h" #include "flutter/shell/platform/darwin/ios/platform_view_ios.h" +static double kTouchTrackerCheckInterval = 1.f; + @interface FlutterViewController () @property(nonatomic, readonly) NSMutableDictionary* pluginPublications; +@property(nonatomic, retain) NSMutableSet* touchTrackerSet; +@property(nonatomic, retain) NSMutableDictionary* touchTrackerDict; + @end @interface FlutterViewControllerRegistrar : NSObject @@ -70,6 +75,9 @@ - (instancetype)initWithProject:(FlutterDartProject*)projectOrNil else _dartProject.reset([projectOrNil retain]); + _touchTrackerSet = [[NSMutableSet set] retain]; + _touchTrackerDict = [[NSMutableDictionary dictionary] retain]; + [self performCommonViewControllerInitialization]; } @@ -476,6 +484,9 @@ - (void)viewDidDisappear:(BOOL)animated { - (void)dealloc { [[NSNotificationCenter defaultCenter] removeObserver:self]; [_pluginPublications release]; + [_touchTrackerSet release]; + [_touchTrackerDict release]; + [super dealloc]; } @@ -547,12 +558,13 @@ static inline PointerChangeMapperPhase PointerChangePhaseFromUITouchPhase(UITouc return blink::PointerData::DeviceKind::kTouch; } -- (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase { +- (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase trackTouches:(BOOL)bTrack { // Note: we cannot rely on touch.phase, since in some cases, e.g., // handleStatusBarTouches, we synthesize touches from existing events. // // TODO(cbracken) consider creating out own class with the touch fields we // need. + NSTimeInterval tsNow = [[NSDate date] timeIntervalSinceReferenceDate]; auto eventTypePhase = PointerChangePhaseFromUITouchPhase(phase); const CGFloat scale = [UIScreen mainScreen].scale; auto packet = std::make_unique(touches.count); @@ -560,17 +572,37 @@ - (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase { int i = 0; for (UITouch* touch in touches) { int device_id = 0; + NSValue* key = [NSValue valueWithPointer:(void*)touch]; switch (eventTypePhase.second) { - case Accessed: + case Accessed: { device_id = _touchMapper.identifierOf(touch); + if (bTrack) { + [self.touchTrackerSet addObject:touch]; + self.touchTrackerDict[key] = @(tsNow + kTouchTrackerCheckInterval); + } break; - case Added: + } + case Added: { device_id = _touchMapper.registerTouch(touch); + if (bTrack) { + [self.touchTrackerSet addObject:touch]; + self.touchTrackerDict[key] = @(tsNow + kTouchTrackerCheckInterval); + } break; - case Removed: + } + case Removed: { device_id = _touchMapper.unregisterTouch(touch); + if (bTrack) { + [self.touchTrackerDict removeObjectForKey:key]; + [self.touchTrackerSet removeObject:touch]; + } break; + } + } + + if (device_id == 0) { + continue; } FML_DCHECK(device_id != 0); @@ -656,19 +688,44 @@ - (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase { } - (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { - [self dispatchTouches:touches phase:UITouchPhaseBegan]; + [self dispatchTouches:touches phase:UITouchPhaseBegan trackTouches:TRUE]; + [self checkIfCompleteTouches]; } - (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { - [self dispatchTouches:touches phase:UITouchPhaseMoved]; + [self dispatchTouches:touches phase:UITouchPhaseMoved trackTouches:TRUE]; } - (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { - [self dispatchTouches:touches phase:UITouchPhaseEnded]; + [self dispatchTouches:touches phase:UITouchPhaseEnded trackTouches:TRUE]; } - (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { - [self dispatchTouches:touches phase:UITouchPhaseCancelled]; + [self dispatchTouches:touches phase:UITouchPhaseCancelled trackTouches:TRUE]; +} + +- (BOOL)checkIfCompleteTouches { + NSInteger cnt = self.touchTrackerSet.count; + if (cnt <= 0) + return FALSE; + NSTimeInterval tsNow = [[NSDate date] timeIntervalSinceReferenceDate]; + NSSet* tmpTrackingTouches = [self.touchTrackerSet copy]; + NSMutableSet* set = [NSMutableSet set]; + for (UITouch* touch in tmpTrackingTouches) { + NSValue* key = [NSValue valueWithPointer:(void*)touch]; + NSNumber* expiredTime = [self.touchTrackerDict objectForKey:key]; + if (expiredTime.doubleValue <= tsNow) { + [set addObject:touch]; + [self.touchTrackerDict removeObjectForKey:key]; + [self.touchTrackerSet removeObject:touch]; + } + } + if (set.count > 0) { + [self dispatchTouches:set phase:UITouchPhaseBegan trackTouches:FALSE]; + [self dispatchTouches:set phase:UITouchPhaseCancelled trackTouches:FALSE]; + return TRUE; + } + return FALSE; } #pragma mark - Handle view resizing @@ -973,8 +1030,8 @@ - (void)handleStatusBarTouches:(UIEvent*)event { CGPoint screenLoc = [touch.window convertPoint:windowLoc toWindow:nil]; if (CGRectContainsPoint(statusBarFrame, screenLoc)) { NSSet* statusbarTouches = [NSSet setWithObject:touch]; - [self dispatchTouches:statusbarTouches phase:UITouchPhaseBegan]; - [self dispatchTouches:statusbarTouches phase:UITouchPhaseEnded]; + [self dispatchTouches:statusbarTouches phase:UITouchPhaseBegan trackTouches:TRUE]; + [self dispatchTouches:statusbarTouches phase:UITouchPhaseEnded trackTouches:TRUE]; return; } } diff --git a/shell/platform/darwin/ios/framework/Source/flutter_touch_mapper.mm b/shell/platform/darwin/ios/framework/Source/flutter_touch_mapper.mm index b0a69b03acd47..cb76466ca0969 100644 --- a/shell/platform/darwin/ios/framework/Source/flutter_touch_mapper.mm +++ b/shell/platform/darwin/ios/framework/Source/flutter_touch_mapper.mm @@ -11,6 +11,9 @@ TouchMapper::~TouchMapper() = default; int TouchMapper::registerTouch(UITouch* touch) { + if (touch_map_.find(touch) != touch_map_.end()) { + return 0; + } int freeSpot = ffsll(free_spots_); touch_map_[touch] = freeSpot; free_spots_ &= ~(1 << (freeSpot - 1)); @@ -18,6 +21,9 @@ } int TouchMapper::unregisterTouch(UITouch* touch) { + if (touch_map_.find(touch) == touch_map_.end()) { + return 0; + } auto index = touch_map_[touch]; free_spots_ |= 1 << (index - 1); touch_map_.erase(touch); @@ -25,6 +31,9 @@ } int TouchMapper::identifierOf(UITouch* touch) const { + if (touch_map_.find(touch) == touch_map_.end()) { + return 0; + } return touch_map_.at(touch); }