FlutterViewController.mm 20.1 KB
Newer Older
1
// Copyright 2016 The Chromium Authors. All rights reserved.
C
Chinmay Garde 已提交
2 3 4
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

5
#import "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
C
Chinmay Garde 已提交
6

A
Adam Barth 已提交
7 8
#include <memory>

9 10 11
#include "base/mac/scoped_block.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
A
Adam Barth 已提交
12
#include "flutter/common/threads.h"
13
#include "flutter/shell/platform/darwin/common/platform_mac.h"
14
#include "flutter/shell/platform/darwin/common/string_conversions.h"
15
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
16
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h"
17 18
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
19
#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h"
20
#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_touch_mapper.h"
21
#include "flutter/shell/platform/darwin/ios/platform_view_ios.h"
A
Adam Barth 已提交
22
#include "lib/ftl/functional/make_copyable.h"
23
#include "lib/ftl/time/time_delta.h"
24

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
namespace {

typedef void (^PlatformMessageResponseCallback)(NSString*);

class PlatformMessageResponseDarwin : public blink::PlatformMessageResponse {
  FRIEND_MAKE_REF_COUNTED(PlatformMessageResponseDarwin);

 public:
  void Complete(std::vector<uint8_t> data) override {
    ftl::RefPtr<PlatformMessageResponseDarwin> self(this);
    blink::Threads::Platform()->PostTask(
        ftl::MakeCopyable([ self, data = std::move(data) ]() mutable {
          self->callback_.get()(shell::GetNSStringFromVector(data));
        }));
  }

  void CompleteWithError() override { Complete(std::vector<uint8_t>()); }

 private:
44 45
  explicit PlatformMessageResponseDarwin(
      PlatformMessageResponseCallback callback)
46 47 48 49 50 51 52
      : callback_(callback, base::scoped_policy::RETAIN) {}

  base::mac::ScopedBlock<PlatformMessageResponseCallback> callback_;
};

}  // namespace

53 54
@interface FlutterViewController ()<UIAlertViewDelegate,
                                    FlutterTextInputDelegate>
55 56
@end

57
void FlutterInit(int argc, const char* argv[]) {
58 59 60
  // Deprecated. To be removed.
}

61
@implementation FlutterViewController {
62
  base::scoped_nsprotocol<FlutterDartProject*> _dartProject;
63
  UIInterfaceOrientationMask _orientationPreferences;
64
  UIStatusBarStyle _statusBarStyle;
65
  blink::ViewportMetrics _viewportMetrics;
66 67
  shell::TouchMapper _touchMapper;
  std::unique_ptr<shell::PlatformViewIOS> _platformView;
68
  base::scoped_nsprotocol<FlutterPlatformPlugin*> _platformPlugin;
69
  base::scoped_nsprotocol<FlutterTextInputPlugin*> _textInputPlugin;
70

71
  BOOL _initialized;
72 73
}

74 75 76 77 78 79
+ (void)initialize {
  if (self == [FlutterViewController class]) {
    shell::FlutterMain();
  }
}

80 81
#pragma mark - Manage and override all designated initializers

82 83 84 85
- (instancetype)initWithProject:(FlutterDartProject*)project
                        nibName:(NSString*)nibNameOrNil
                         bundle:(NSBundle*)nibBundleOrNil {
  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
86

87
  if (self) {
88
    if (project == nil)
89 90
      _dartProject.reset(
          [[FlutterDartProject alloc] initFromDefaultSourceForConfiguration]);
91 92
    else
      _dartProject.reset([project retain]);
93 94

    [self performCommonViewControllerInitialization];
95
  }
96

97
  return self;
C
Chinmay Garde 已提交
98 99
}

100 101
- (instancetype)initWithNibName:(NSString*)nibNameOrNil
                         bundle:(NSBundle*)nibBundleOrNil {
102
  return [self initWithProject:nil nibName:nil bundle:nil];
C
Chinmay Garde 已提交
103 104
}

105
- (instancetype)initWithCoder:(NSCoder*)aDecoder {
106
  return [self initWithProject:nil nibName:nil bundle:nil];
107 108
}

109
#pragma mark - Common view controller initialization tasks
C
Chinmay Garde 已提交
110

111
- (void)performCommonViewControllerInitialization {
112
  if (_initialized)
113
    return;
114

115
  _initialized = YES;
C
Chinmay Garde 已提交
116

117
  _orientationPreferences = UIInterfaceOrientationMaskAll;
118
  _statusBarStyle = UIStatusBarStyleDefault;
A
Adam Barth 已提交
119
  _platformView = std::make_unique<shell::PlatformViewIOS>(
120 121
      reinterpret_cast<CAEAGLLayer*>(self.view.layer));
  _platformView->SetupResourceContextOnIOThread();
C
Chinmay Garde 已提交
122

123 124 125
  _platformPlugin.reset([[FlutterPlatformPlugin alloc] init]);
  [self addMessageListener:_platformPlugin.get()];

126 127 128 129
  _textInputPlugin.reset([[FlutterTextInputPlugin alloc] init]);
  _textInputPlugin.get().textInputDelegate = self;
  [self addMessageListener:_textInputPlugin.get()];

130
  [self setupNotificationCenterObservers];
C
Chinmay Garde 已提交
131

132
  [self connectToEngineAndLoad];
C
Chinmay Garde 已提交
133 134
}

135 136 137 138
- (void)setupNotificationCenterObservers {
  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  [center addObserver:self
             selector:@selector(onOrientationPreferencesUpdated:)
A
Adam Barth 已提交
139
                 name:@(shell::kOrientationUpdateNotificationName)
140 141
               object:nil];

142 143
  [center addObserver:self
             selector:@selector(onPreferredStatusBarStyleUpdated:)
A
Adam Barth 已提交
144
                 name:@(shell::kOverlayStyleUpdateNotificationName)
145 146
               object:nil];

147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
  [center addObserver:self
             selector:@selector(applicationBecameActive:)
                 name:UIApplicationDidBecomeActiveNotification
               object:nil];

  [center addObserver:self
             selector:@selector(applicationWillResignActive:)
                 name:UIApplicationWillResignActiveNotification
               object:nil];

  [center addObserver:self
             selector:@selector(keyboardWasShown:)
                 name:UIKeyboardDidShowNotification
               object:nil];

  [center addObserver:self
             selector:@selector(keyboardWillBeHidden:)
                 name:UIKeyboardWillHideNotification
               object:nil];

  [center addObserver:self
             selector:@selector(onLocaleUpdated:)
                 name:NSCurrentLocaleDidChangeNotification
               object:nil];
171 172 173 174 175

  [center addObserver:self
             selector:@selector(onVoiceOverChanged:)
                 name:UIAccessibilityVoiceOverStatusChanged
               object:nil];
C
Chinmay Garde 已提交
176 177
}

178
#pragma mark - Initializing the engine
179

180
- (void)alertView:(UIAlertView*)alertView
181
    clickedButtonAtIndex:(NSInteger)buttonIndex {
182 183 184
  exit(0);
}

185
- (void)connectToEngineAndLoad {
C
Chinmay Garde 已提交
186
  TRACE_EVENT0("flutter", "connectToEngineAndLoad");
187

188
  // We ask the VM to check what it supports.
189 190
  const enum VMType type =
      Dart_IsPrecompiledRuntime() ? VMTypePrecompilation : VMTypeInterpreter;
191

A
Adam Barth 已提交
192
  [_dartProject launchInEngine:&_platformView->engine()
193 194 195 196 197 198
                embedderVMType:type
                        result:^(BOOL success, NSString* message) {
                          if (!success) {
                            UIAlertView* alert = [[UIAlertView alloc]
                                    initWithTitle:@"Launch Error"
                                          message:message
199
                                         delegate:self
200 201 202 203 204 205
                                cancelButtonTitle:@"OK"
                                otherButtonTitles:nil];
                            [alert show];
                            [alert release];
                          }
                        }];
206 207 208 209 210
}

#pragma mark - Loading the view

- (void)loadView {
211
  FlutterView* view = [[FlutterView alloc] init];
212

213
  self.view = view;
214 215 216 217
  self.view.multipleTouchEnabled = YES;
  self.view.autoresizingMask =
      UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

218
  [view release];
219 220 221 222 223
}

#pragma mark - Application lifecycle notifications

- (void)applicationBecameActive:(NSNotification*)notification {
224 225
  [self sendString:@"AppLifecycleState.resumed"
      withMessageName:@"flutter/lifecycle"];
226 227 228
}

- (void)applicationWillResignActive:(NSNotification*)notification {
229 230
  [self sendString:@"AppLifecycleState.paused"
      withMessageName:@"flutter/lifecycle"];
231 232 233 234 235 236 237 238 239 240
}

#pragma mark - Touch event handling

enum MapperPhase {
  Accessed,
  Added,
  Removed,
};

241 242
using PointerChangeMapperPhase =
    std::pair<blink::PointerData::Change, MapperPhase>;
A
Adam Barth 已提交
243
static inline PointerChangeMapperPhase PointerChangePhaseFromUITouchPhase(
244 245 246
    UITouchPhase phase) {
  switch (phase) {
    case UITouchPhaseBegan:
A
Adam Barth 已提交
247 248
      return PointerChangeMapperPhase(blink::PointerData::Change::kDown,
                                      MapperPhase::Added);
249 250 251 252
    case UITouchPhaseMoved:
    case UITouchPhaseStationary:
      // There is no EVENT_TYPE_POINTER_STATIONARY. So we just pass a move type
      // with the same coordinates
A
Adam Barth 已提交
253 254
      return PointerChangeMapperPhase(blink::PointerData::Change::kMove,
                                      MapperPhase::Accessed);
255
    case UITouchPhaseEnded:
A
Adam Barth 已提交
256 257
      return PointerChangeMapperPhase(blink::PointerData::Change::kUp,
                                      MapperPhase::Removed);
258
    case UITouchPhaseCancelled:
A
Adam Barth 已提交
259 260
      return PointerChangeMapperPhase(blink::PointerData::Change::kCancel,
                                      MapperPhase::Removed);
261 262
  }

A
Adam Barth 已提交
263 264
  return PointerChangeMapperPhase(blink::PointerData::Change::kCancel,
                                  MapperPhase::Accessed);
265
}
C
Chinmay Garde 已提交
266 267

- (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase {
268 269 270 271 272
  // 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.
A
Adam Barth 已提交
273
  auto eventTypePhase = PointerChangePhaseFromUITouchPhase(phase);
C
Chinmay Garde 已提交
274
  const CGFloat scale = [UIScreen mainScreen].scale;
A
Adam Barth 已提交
275
  auto packet = std::make_unique<blink::PointerDataPacket>(touches.count);
C
Chinmay Garde 已提交
276

A
Adam Barth 已提交
277
  int i = 0;
C
Chinmay Garde 已提交
278
  for (UITouch* touch in touches) {
279
    int device_id = 0;
280 281 282

    switch (eventTypePhase.second) {
      case Accessed:
283
        device_id = _touchMapper.identifierOf(touch);
284 285
        break;
      case Added:
286
        device_id = _touchMapper.registerTouch(touch);
287 288
        break;
      case Removed:
289
        device_id = _touchMapper.unregisterTouch(touch);
290 291
        break;
    }
292

293
    DCHECK(device_id != 0);
C
Chinmay Garde 已提交
294
    CGPoint windowCoordinates = [touch locationInView:nil];
295

A
Adam Barth 已提交
296 297 298
    blink::PointerData pointer_data;
    pointer_data.Clear();

299 300
    constexpr int kMicrosecondsPerSecond = 1000 * 1000;
    pointer_data.time_stamp = touch.timestamp * kMicrosecondsPerSecond;
A
Adam Barth 已提交
301 302
    pointer_data.change = eventTypePhase.first;
    pointer_data.kind = blink::PointerData::DeviceKind::kTouch;
303
    pointer_data.device = device_id;
A
Adam Barth 已提交
304 305 306 307 308 309
    pointer_data.physical_x = windowCoordinates.x * scale;
    pointer_data.physical_y = windowCoordinates.y * scale;
    pointer_data.pressure = 1.0;
    pointer_data.pressure_max = 1.0;

    packet->SetPointerData(i++, pointer_data);
C
Chinmay Garde 已提交
310
  }
311

A
Adam Barth 已提交
312
  blink::Threads::UI()->PostTask(ftl::MakeCopyable([
313 314 315 316 317
    engine = _platformView->engine().GetWeakPtr(), packet = std::move(packet)
  ] {
    if (engine.get())
      engine->DispatchPointerDataPacket(*packet);
  }));
C
Chinmay Garde 已提交
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335
}

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

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

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

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

336
#pragma mark - Handle view resizing
337

338 339
- (void)updateViewportMetrics {
  blink::Threads::UI()->PostTask([
340
    weak_platform_view = _platformView->GetWeakPtr(), metrics = _viewportMetrics
341
  ] {
342 343 344 345 346
    if (!weak_platform_view) {
      return;
    }
    weak_platform_view->UpdateSurfaceSize();
    weak_platform_view->engine().SetViewportMetrics(metrics);
347 348 349
  });
}

350
- (bool)isWindowFullscreen {
351
  UIWindow* window = self.view.window;
352 353 354 355 356 357 358 359 360 361 362 363 364 365
  return CGRectEqualToRect(window.frame, window.screen.bounds);
}

- (CGFloat)statusBarPadding {
  // If we're a child of a containing view, let the container apply padding.
  if (self.parentViewController != nil) {
    return 0.0;
  }

  // If not fullscreen, assume we don't want padding.
  if (![self isWindowFullscreen]) {
    return 0.0;
  }

366
  UIScreen* screen = self.view.window.screen;
367 368 369
  CGRect statusFrame = [UIApplication sharedApplication].statusBarFrame;
  CGRect viewFrame = [self.view convertRect:self.view.bounds
                          toCoordinateSpace:screen.coordinateSpace];
370 371
  CGFloat padding =
      statusFrame.origin.y + statusFrame.size.height - viewFrame.origin.y;
372 373 374
  return MAX(padding, 0.0);
}

375
- (void)viewDidLayoutSubviews {
376
  CGSize viewSize = self.view.bounds.size;
377 378
  CGFloat scale = [UIScreen mainScreen].scale;

379
  _viewportMetrics.device_pixel_ratio = scale;
380 381
  _viewportMetrics.physical_width = viewSize.width * scale;
  _viewportMetrics.physical_height = viewSize.height * scale;
382
  _viewportMetrics.physical_padding_top = [self statusBarPadding] * scale;
383
  [self updateViewportMetrics];
384 385
}

386
#pragma mark - Keyboard events
387

388 389 390 391 392
- (void)keyboardWasShown:(NSNotification*)notification {
  NSDictionary* info = [notification userInfo];
  CGFloat bottom = CGRectGetHeight(
      [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]);
  CGFloat scale = [UIScreen mainScreen].scale;
393 394
  _viewportMetrics.physical_padding_bottom = bottom * scale;
  [self updateViewportMetrics];
395 396
}

397
- (void)keyboardWillBeHidden:(NSNotification*)notification {
398 399
  _viewportMetrics.physical_padding_bottom = 0;
  [self updateViewportMetrics];
400 401
}

402 403 404
#pragma mark - Text input delegate

- (void)updateEditingClient:(int)client withState:(NSDictionary*)state {
405 406 407 408 409
  NSDictionary* message = @{
    @"method" : @"TextInputClient.updateEditingState",
    @"args" : @[ @(client), state ],
  };
  [self sendJSON:message withMessageName:@"flutter/textinputclient"];
410 411
}

412 413 414 415 416 417 418
#pragma mark - Orientation updates

- (void)onOrientationPreferencesUpdated:(NSNotification*)notification {
  // Notifications may not be on the iOS UI thread
  dispatch_async(dispatch_get_main_queue(), ^{
    NSDictionary* info = notification.userInfo;

419
    NSNumber* update = info[@(shell::kOrientationUpdateNotificationKey)];
420 421 422 423 424 425 426 427 428 429 430 431

    if (update == nil) {
      return;
    }

    NSUInteger new_preferences = update.unsignedIntegerValue;

    if (new_preferences != _orientationPreferences) {
      _orientationPreferences = new_preferences;
      [UIViewController attemptRotationToDeviceOrientation];
    }
  });
432 433
}

434 435
- (BOOL)shouldAutorotate {
  return YES;
436 437
}

438 439 440 441
- (NSUInteger)supportedInterfaceOrientations {
  return _orientationPreferences;
}

442 443 444 445 446 447 448
#pragma mark - Accessibility

- (void)onVoiceOverChanged:(NSNotification*)notification {
#if TARGET_OS_SIMULATOR
  // There doesn't appear to be any way to determine whether the accessibility
  // inspector is enabled on the simulator. We conservatively always turn on the
  // accessibility bridge in the simulator.
449
  bool enabled = true;
450
#else
451
  bool enabled = UIAccessibilityIsVoiceOverRunning();
452
#endif
453
  _platformView->ToggleAccessibility(self.view, enabled);
454 455
}

456 457 458 459 460 461
#pragma mark - Locale updates

- (void)onLocaleUpdated:(NSNotification*)notification {
  NSLocale* currentLocale = [NSLocale currentLocale];
  NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode];
  NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
462 463 464 465
  NSDictionary* message =
      @{ @"method" : @"setLocale",
         @"args" : @[ languageCode, countryCode ] };
  [self sendJSON:message withMessageName:@"flutter/localization"];
466 467 468 469 470
}

#pragma mark - Surface creation and teardown updates

- (void)surfaceUpdated:(BOOL)appeared {
471
  CHECK(_platformView != nullptr);
472 473

  if (appeared) {
474
    _platformView->NotifyCreated();
475
  } else {
476
    _platformView->NotifyDestroyed();
477 478 479
  }
}

480 481
- (void)viewDidAppear:(BOOL)animated {
  [self surfaceUpdated:YES];
482 483
  [self onLocaleUpdated:nil];
  [self onVoiceOverChanged:nil];
484

485
  [super viewDidAppear:animated];
486
}
C
Chinmay Garde 已提交
487

488 489
- (void)viewWillDisappear:(BOOL)animated {
  [self surfaceUpdated:NO];
490 491

  [super viewWillDisappear:animated];
492 493
}

C
Chinmay Garde 已提交
494
- (void)dealloc {
495
  [[NSNotificationCenter defaultCenter] removeObserver:self];
496 497
  [super dealloc];
}
498

499 500 501 502 503
#pragma mark - Status Bar touch event handling

// Standard iOS status bar height in pixels.
constexpr CGFloat kStandardStatusBarHeight = 20.0;

504
- (void)handleStatusBarTouches:(UIEvent*)event {
505 506 507 508 509 510 511 512
  // If the status bar is double-height, don't handle status bar taps. iOS
  // should open the app associated with the status bar.
  CGRect statusBarFrame = [UIApplication sharedApplication].statusBarFrame;
  if (statusBarFrame.size.height != kStandardStatusBarHeight) {
    return;
  }

  // If we detect a touch in the status bar, synthesize a fake touch begin/end.
513
  for (UITouch* touch in event.allTouches) {
514 515 516 517
    if (touch.phase == UITouchPhaseBegan && touch.tapCount > 0) {
      CGPoint windowLoc = [touch locationInView:nil];
      CGPoint screenLoc = [touch.window convertPoint:windowLoc toWindow:nil];
      if (CGRectContainsPoint(statusBarFrame, screenLoc)) {
518
        NSSet* statusbarTouches = [NSSet setWithObject:touch];
519 520 521 522 523 524 525 526
        [self dispatchTouches:statusbarTouches phase:UITouchPhaseBegan];
        [self dispatchTouches:statusbarTouches phase:UITouchPhaseEnded];
        return;
      }
    }
  }
}

527 528 529 530 531 532 533 534 535 536 537
#pragma mark - Status bar style

- (UIStatusBarStyle)preferredStatusBarStyle {
  return _statusBarStyle;
}

- (void)onPreferredStatusBarStyleUpdated:(NSNotification*)notification {
  // Notifications may not be on the iOS UI thread
  dispatch_async(dispatch_get_main_queue(), ^{
    NSDictionary* info = notification.userInfo;

538
    NSNumber* update = info[@(shell::kOverlayStyleUpdateNotificationKey)];
539 540 541 542 543 544 545 546 547 548 549 550 551 552

    if (update == nil) {
      return;
    }

    NSInteger style = update.integerValue;

    if (style != _statusBarStyle) {
      _statusBarStyle = static_cast<UIStatusBarStyle>(style);
      [self setNeedsStatusBarAppearanceUpdate];
    }
  });
}

553
#pragma mark - Application Messages
554

555
- (void)sendString:(NSString*)message withMessageName:(NSString*)channel {
556
  NSAssert(message, @"The message must not be null");
557 558 559
  NSAssert(channel, @"The channel must not be null");
  _platformView->DispatchPlatformMessage(
      ftl::MakeRefCounted<blink::PlatformMessage>(
560
          channel.UTF8String, shell::GetVectorFromNSString(message), nullptr));
561 562 563
}

- (void)sendString:(NSString*)message
564
    withMessageName:(NSString*)channel
565
           callback:(void (^)(NSString*))callback {
566
  NSAssert(message, @"The message must not be null");
567
  NSAssert(channel, @"The channel must not be null");
568
  NSAssert(callback, @"The callback must not be null");
569 570
  _platformView->DispatchPlatformMessage(
      ftl::MakeRefCounted<blink::PlatformMessage>(
571
          channel.UTF8String, shell::GetVectorFromNSString(message),
572
          ftl::MakeRefCounted<PlatformMessageResponseDarwin>(callback)));
573 574
}

575
- (void)sendJSON:(NSDictionary*)message withMessageName:(NSString*)channel {
576 577
  NSData* data =
      [NSJSONSerialization dataWithJSONObject:message options:0 error:nil];
578 579
  if (!data)
    return;
580
  const uint8_t* bytes = static_cast<const uint8_t*>(data.bytes);
581 582
  _platformView->DispatchPlatformMessage(
      ftl::MakeRefCounted<blink::PlatformMessage>(
583
          channel.UTF8String, std::vector<uint8_t>(bytes, bytes + data.length),
584 585 586
          nullptr));
}

587
- (void)addMessageListener:(NSObject<FlutterMessageListener>*)listener {
588
  NSAssert(listener, @"The listener must not be null");
589 590
  NSString* channel = listener.messageName;
  NSAssert(channel, @"The channel must not be null");
591 592
  _platformView->platform_message_router().SetMessageListener(
      channel.UTF8String, listener);
593 594
}

595
- (void)removeMessageListener:(NSObject<FlutterMessageListener>*)listener {
596
  NSAssert(listener, @"The listener must not be null");
597 598
  NSString* channel = listener.messageName;
  NSAssert(channel, @"The channel must not be null");
599 600
  _platformView->platform_message_router().SetMessageListener(
      channel.UTF8String, nil);
601 602
}

603 604
- (void)addAsyncMessageListener:
    (NSObject<FlutterAsyncMessageListener>*)listener {
605 606 607
  NSAssert(listener, @"The listener must not be null");
  NSString* messageName = listener.messageName;
  NSAssert(messageName, @"The messageName must not be null");
608
  _platformView->platform_message_router().SetAsyncMessageListener(
609
      messageName.UTF8String, listener);
610 611
}

612 613
- (void)removeAsyncMessageListener:
    (NSObject<FlutterAsyncMessageListener>*)listener {
614 615 616
  NSAssert(listener, @"The listener must not be null");
  NSString* messageName = listener.messageName;
  NSAssert(messageName, @"The messageName must not be null");
617
  _platformView->platform_message_router().SetAsyncMessageListener(
618
      messageName.UTF8String, nil);
C
Chinmay Garde 已提交
619 620 621
}

@end