FlutterViewController.mm 21.6 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/buffer_conversions.h"
14
#include "flutter/shell/platform/darwin/common/platform_mac.h"
15
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.h"
16
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
17
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h"
18 19
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputDelegate.h"
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterTextInputPlugin.h"
20
#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_main_ios.h"
21
#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_touch_mapper.h"
22
#include "flutter/shell/platform/darwin/ios/platform_view_ios.h"
A
Adam Barth 已提交
23
#include "lib/ftl/functional/make_copyable.h"
24
#include "lib/ftl/time/time_delta.h"
25

26 27
namespace {

28
typedef void (^PlatformMessageResponseCallback)(NSData*);
29 30 31 32 33 34 35 36 37

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 {
38
          self->callback_.get()(shell::GetNSDataFromVector(data));
39 40 41 42 43 44
        }));
  }

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

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

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

}  // namespace

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

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

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

72
  BOOL _initialized;
73 74
}

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

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

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

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

    [self performCommonViewControllerInitialization];
96
  }
97

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

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

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

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

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

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

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

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

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

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

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

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

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

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171
  [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];
172 173 174 175 176

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

179
#pragma mark - Initializing the engine
180

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

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

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

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

#pragma mark - Loading the view

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

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

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

#pragma mark - Application lifecycle notifications

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

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

#pragma mark - Touch event handling

enum MapperPhase {
  Accessed,
  Added,
  Removed,
};

242 243
using PointerChangeMapperPhase =
    std::pair<blink::PointerData::Change, MapperPhase>;
A
Adam Barth 已提交
244
static inline PointerChangeMapperPhase PointerChangePhaseFromUITouchPhase(
245 246 247
    UITouchPhase phase) {
  switch (phase) {
    case UITouchPhaseBegan:
A
Adam Barth 已提交
248 249
      return PointerChangeMapperPhase(blink::PointerData::Change::kDown,
                                      MapperPhase::Added);
250 251 252 253
    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 已提交
254 255
      return PointerChangeMapperPhase(blink::PointerData::Change::kMove,
                                      MapperPhase::Accessed);
256
    case UITouchPhaseEnded:
A
Adam Barth 已提交
257 258
      return PointerChangeMapperPhase(blink::PointerData::Change::kUp,
                                      MapperPhase::Removed);
259
    case UITouchPhaseCancelled:
A
Adam Barth 已提交
260 261
      return PointerChangeMapperPhase(blink::PointerData::Change::kCancel,
                                      MapperPhase::Removed);
262 263
  }

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

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

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

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

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

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

300 301
    constexpr int kMicrosecondsPerSecond = 1000 * 1000;
    pointer_data.time_stamp = touch.timestamp * kMicrosecondsPerSecond;
A
Adam Barth 已提交
302 303
    pointer_data.change = eventTypePhase.first;
    pointer_data.kind = blink::PointerData::DeviceKind::kTouch;
304
    pointer_data.device = device_id;
A
Adam Barth 已提交
305 306 307 308 309 310
    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 已提交
311
  }
312

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

- (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];
}

337
#pragma mark - Handle view resizing
338

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

351
- (CGFloat)statusBarPadding {
352
  UIScreen* screen = self.view.window.screen;
353 354 355
  CGRect statusFrame = [UIApplication sharedApplication].statusBarFrame;
  CGRect viewFrame = [self.view convertRect:self.view.bounds
                          toCoordinateSpace:screen.coordinateSpace];
356 357
  CGRect intersection = CGRectIntersection(statusFrame, viewFrame);
  return CGRectIsNull(intersection) ? 0.0 : intersection.size.height;
358 359
}

360
- (void)viewDidLayoutSubviews {
361
  CGSize viewSize = self.view.bounds.size;
362 363
  CGFloat scale = [UIScreen mainScreen].scale;

364
  _viewportMetrics.device_pixel_ratio = scale;
365 366
  _viewportMetrics.physical_width = viewSize.width * scale;
  _viewportMetrics.physical_height = viewSize.height * scale;
367
  _viewportMetrics.physical_padding_top = [self statusBarPadding] * scale;
368
  [self updateViewportMetrics];
369 370
}

371
#pragma mark - Keyboard events
372

373 374 375 376 377
- (void)keyboardWasShown:(NSNotification*)notification {
  NSDictionary* info = [notification userInfo];
  CGFloat bottom = CGRectGetHeight(
      [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]);
  CGFloat scale = [UIScreen mainScreen].scale;
378 379
  _viewportMetrics.physical_padding_bottom = bottom * scale;
  [self updateViewportMetrics];
380 381
}

382
- (void)keyboardWillBeHidden:(NSNotification*)notification {
383 384
  _viewportMetrics.physical_padding_bottom = 0;
  [self updateViewportMetrics];
385 386
}

387 388 389
#pragma mark - Text input delegate

- (void)updateEditingClient:(int)client withState:(NSDictionary*)state {
390 391 392 393 394
  NSDictionary* message = @{
    @"method" : @"TextInputClient.updateEditingState",
    @"args" : @[ @(client), state ],
  };
  [self sendJSON:message withMessageName:@"flutter/textinputclient"];
395 396
}

397 398 399 400 401 402 403
#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;

404
    NSNumber* update = info[@(shell::kOrientationUpdateNotificationKey)];
405 406 407 408 409 410 411 412 413 414 415 416

    if (update == nil) {
      return;
    }

    NSUInteger new_preferences = update.unsignedIntegerValue;

    if (new_preferences != _orientationPreferences) {
      _orientationPreferences = new_preferences;
      [UIViewController attemptRotationToDeviceOrientation];
    }
  });
417 418
}

419 420
- (BOOL)shouldAutorotate {
  return YES;
421 422
}

423 424 425 426
- (NSUInteger)supportedInterfaceOrientations {
  return _orientationPreferences;
}

427 428 429 430 431 432 433
#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.
434
  bool enabled = true;
435
#else
436
  bool enabled = UIAccessibilityIsVoiceOverRunning();
437
#endif
438
  _platformView->ToggleAccessibility(self.view, enabled);
439 440
}

441 442 443 444 445 446
#pragma mark - Locale updates

- (void)onLocaleUpdated:(NSNotification*)notification {
  NSLocale* currentLocale = [NSLocale currentLocale];
  NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode];
  NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
447 448 449 450
  NSDictionary* message =
      @{ @"method" : @"setLocale",
         @"args" : @[ languageCode, countryCode ] };
  [self sendJSON:message withMessageName:@"flutter/localization"];
451 452 453 454 455
}

#pragma mark - Surface creation and teardown updates

- (void)surfaceUpdated:(BOOL)appeared {
456
  CHECK(_platformView != nullptr);
457 458

  if (appeared) {
459
    _platformView->NotifyCreated();
460
  } else {
461
    _platformView->NotifyDestroyed();
462 463 464
  }
}

465 466
- (void)viewDidAppear:(BOOL)animated {
  [self surfaceUpdated:YES];
467 468
  [self onLocaleUpdated:nil];
  [self onVoiceOverChanged:nil];
469

470
  [super viewDidAppear:animated];
471
}
C
Chinmay Garde 已提交
472

473 474
- (void)viewWillDisappear:(BOOL)animated {
  [self surfaceUpdated:NO];
475 476

  [super viewWillDisappear:animated];
477 478
}

C
Chinmay Garde 已提交
479
- (void)dealloc {
480
  [[NSNotificationCenter defaultCenter] removeObserver:self];
481 482
  [super dealloc];
}
483

484 485 486 487 488
#pragma mark - Status Bar touch event handling

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

489
- (void)handleStatusBarTouches:(UIEvent*)event {
490 491 492 493 494 495 496 497
  // 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.
498
  for (UITouch* touch in event.allTouches) {
499 500 501 502
    if (touch.phase == UITouchPhaseBegan && touch.tapCount > 0) {
      CGPoint windowLoc = [touch locationInView:nil];
      CGPoint screenLoc = [touch.window convertPoint:windowLoc toWindow:nil];
      if (CGRectContainsPoint(statusBarFrame, screenLoc)) {
503
        NSSet* statusbarTouches = [NSSet setWithObject:touch];
504 505 506 507 508 509 510 511
        [self dispatchTouches:statusbarTouches phase:UITouchPhaseBegan];
        [self dispatchTouches:statusbarTouches phase:UITouchPhaseEnded];
        return;
      }
    }
  }
}

512 513 514 515 516 517 518 519 520 521 522
#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;

523
    NSNumber* update = info[@(shell::kOverlayStyleUpdateNotificationKey)];
524 525 526 527 528 529 530 531 532 533 534 535 536 537

    if (update == nil) {
      return;
    }

    NSInteger style = update.integerValue;

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

538
#pragma mark - Application Messages
539

540
- (void)sendString:(NSString*)message withMessageName:(NSString*)channel {
541
  NSAssert(message, @"The message must not be null");
542
  NSAssert(channel, @"The channel must not be null");
543 544
  FlutterStringCodec* codec = [FlutterStringCodec sharedInstance];
  [self sendBinaryMessage:[codec encode:message] channelName:channel];
545 546 547
}

- (void)sendString:(NSString*)message
548
    withMessageName:(NSString*)channel
549
           callback:(void (^)(NSString*))callback {
550
  NSAssert(message, @"The message must not be null");
551
  NSAssert(channel, @"The channel must not be null");
552
  NSAssert(callback, @"The callback must not be null");
553 554 555 556 557 558
  FlutterStringCodec* codec = [FlutterStringCodec sharedInstance];
  [self sendBinaryMessage:[codec encode:message]
              channelName:channel
       binaryReplyHandler:^(NSData* data) {
         callback([codec decode:data]);
       }];
559 560
}

561
- (void)sendJSON:(NSDictionary*)message withMessageName:(NSString*)channel {
562 563
  NSData* data = [[FlutterJSONMessageCodec sharedInstance] encode:message];
  [self sendBinaryMessage:data channelName:channel];
564 565
}

566
- (void)addMessageListener:(NSObject<FlutterMessageListener>*)listener {
567
  NSAssert(listener, @"The listener must not be null");
568 569 570 571 572 573 574 575 576 577 578
  NSString* messageName = listener.messageName;
  NSAssert(messageName, @"The messageName must not be null");
  FlutterStringCodec* codec = [FlutterStringCodec sharedInstance];
  [self
      setBinaryMessageHandlerOnChannel:messageName
                  binaryMessageHandler:^(
                      NSData* message, FlutterBinaryReplyHandler replyHandler) {
                    NSString* reply =
                        [listener didReceiveString:[codec decode:message]];
                    replyHandler([codec encode:reply]);
                  }];
579 580
}

581
- (void)removeMessageListener:(NSObject<FlutterMessageListener>*)listener {
582
  NSAssert(listener, @"The listener must not be null");
583 584 585
  NSString* messageName = listener.messageName;
  NSAssert(messageName, @"The messageName must not be null");
  [self setBinaryMessageHandlerOnChannel:messageName binaryMessageHandler:nil];
586 587
}

588 589
- (void)addAsyncMessageListener:
    (NSObject<FlutterAsyncMessageListener>*)listener {
590 591 592
  NSAssert(listener, @"The listener must not be null");
  NSString* messageName = listener.messageName;
  NSAssert(messageName, @"The messageName must not be null");
593 594 595 596 597 598 599 600 601 602
  FlutterStringCodec* codec = [FlutterStringCodec sharedInstance];
  [self
      setBinaryMessageHandlerOnChannel:messageName
                  binaryMessageHandler:^(
                      NSData* message, FlutterBinaryReplyHandler replyHandler) {
                    [listener didReceiveString:[codec decode:message]
                                      callback:^(NSString* reply) {
                                        replyHandler([codec encode:reply]);
                                      }];
                  }];
603 604
}

605 606
- (void)removeAsyncMessageListener:
    (NSObject<FlutterAsyncMessageListener>*)listener {
607 608 609
  NSAssert(listener, @"The listener must not be null");
  NSString* messageName = listener.messageName;
  NSAssert(messageName, @"The messageName must not be null");
610
  [self setBinaryMessageHandlerOnChannel:messageName binaryMessageHandler:nil];
C
Chinmay Garde 已提交
611 612
}

613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641
- (void)sendBinaryMessage:(NSData*)message channelName:(NSString*)channel {
  NSAssert(message, @"The message must not be null");
  NSAssert(channel, @"The channel must not be null");
  _platformView->DispatchPlatformMessage(
      ftl::MakeRefCounted<blink::PlatformMessage>(
          channel.UTF8String, shell::GetVectorFromNSData(message), nil));
}

- (void)sendBinaryMessage:(NSData*)message
              channelName:(NSString*)channel
       binaryReplyHandler:(FlutterBinaryReplyHandler)callback {
  NSAssert(message, @"The message must not be null");
  NSAssert(channel, @"The channel must not be null");
  NSAssert(callback, @"The callback must not be null");
  _platformView->DispatchPlatformMessage(
      ftl::MakeRefCounted<blink::PlatformMessage>(
          channel.UTF8String, shell::GetVectorFromNSData(message),
          ftl::MakeRefCounted<PlatformMessageResponseDarwin>(^(NSData* reply) {
            if (callback)
              callback(reply);
          })));
}

- (void)setBinaryMessageHandlerOnChannel:(NSString*)channel
                    binaryMessageHandler:(FlutterBinaryMessageHandler)handler {
  NSAssert(channel, @"The channel name must not be null");
  _platformView->platform_message_router().SetMessageHandler(channel.UTF8String,
                                                             handler);
}
C
Chinmay Garde 已提交
642
@end