FlutterViewController.mm 20.4 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 73 74 75 76
  base::scoped_nsprotocol<FlutterMethodChannel*> _localizationChannel;
  base::scoped_nsprotocol<FlutterMethodChannel*> _navigationChannel;
  base::scoped_nsprotocol<FlutterMethodChannel*> _platformChannel;
  base::scoped_nsprotocol<FlutterMethodChannel*> _textInputChannel;
  base::scoped_nsprotocol<FlutterMessageChannel*> _lifecycleChannel;
  base::scoped_nsprotocol<FlutterMessageChannel*> _systemChannel;
77
  BOOL _initialized;
78 79
}

80 81 82 83 84 85
+ (void)initialize {
  if (self == [FlutterViewController class]) {
    shell::FlutterMain();
  }
}

86 87
#pragma mark - Manage and override all designated initializers

88 89 90 91
- (instancetype)initWithProject:(FlutterDartProject*)project
                        nibName:(NSString*)nibNameOrNil
                         bundle:(NSBundle*)nibBundleOrNil {
  self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
92

93
  if (self) {
94
    if (project == nil)
95 96
      _dartProject.reset(
          [[FlutterDartProject alloc] initFromDefaultSourceForConfiguration]);
97 98
    else
      _dartProject.reset([project retain]);
99 100

    [self performCommonViewControllerInitialization];
101
  }
102

103
  return self;
C
Chinmay Garde 已提交
104 105
}

106 107
- (instancetype)initWithNibName:(NSString*)nibNameOrNil
                         bundle:(NSBundle*)nibBundleOrNil {
108
  return [self initWithProject:nil nibName:nil bundle:nil];
C
Chinmay Garde 已提交
109 110
}

111
- (instancetype)initWithCoder:(NSCoder*)aDecoder {
112
  return [self initWithProject:nil nibName:nil bundle:nil];
113 114
}

115
#pragma mark - Common view controller initialization tasks
C
Chinmay Garde 已提交
116

117
- (void)performCommonViewControllerInitialization {
118
  if (_initialized)
119
    return;
120

121
  _initialized = YES;
C
Chinmay Garde 已提交
122

123
  _orientationPreferences = UIInterfaceOrientationMaskAll;
124
  _statusBarStyle = UIStatusBarStyleDefault;
A
Adam Barth 已提交
125
  _platformView = std::make_unique<shell::PlatformViewIOS>(
126
    reinterpret_cast<CAEAGLLayer*>(self.view.layer));
127
  _platformView->SetupResourceContextOnIOThread();
C
Chinmay Garde 已提交
128

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
  _localizationChannel.reset([[FlutterMethodChannel alloc]
      initWithName:@"flutter/localization"
      binaryMessenger:self
      codec:[FlutterJSONMethodCodec sharedInstance]]);

  _navigationChannel.reset([[FlutterMethodChannel alloc]
      initWithName:@"flutter/navigation"
      binaryMessenger:self
      codec:[FlutterJSONMethodCodec sharedInstance]]);

  _platformChannel.reset([[FlutterMethodChannel alloc]
      initWithName:@"flutter/platform"
      binaryMessenger:self
      codec:[FlutterJSONMethodCodec sharedInstance]]);

  _textInputChannel.reset([[FlutterMethodChannel alloc]
      initWithName:@"flutter/textinput"
      binaryMessenger:self
      codec:[FlutterJSONMethodCodec sharedInstance]]);

  _lifecycleChannel.reset([[FlutterMessageChannel alloc]
      initWithName:@"flutter/lifecycle"
      binaryMessenger:self
      codec:[FlutterStringCodec sharedInstance]]);

  _systemChannel.reset([[FlutterMessageChannel alloc]
      initWithName:@"flutter/system"
      binaryMessenger:self
      codec:[FlutterJSONMessageCodec sharedInstance]]);

159
  _platformPlugin.reset([[FlutterPlatformPlugin alloc] init]);
160 161 162
  [_platformChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResultReceiver resultReceiver) {
   [_platformPlugin.get() handleMethodCall:call resultReceiver:resultReceiver];
  }];
163

164 165
  _textInputPlugin.reset([[FlutterTextInputPlugin alloc] init]);
  _textInputPlugin.get().textInputDelegate = self;
166 167 168
  [_textInputChannel.get() setMethodCallHandler:^(FlutterMethodCall* call, FlutterResultReceiver resultReceiver) {
   [_textInputPlugin.get() handleMethodCall:call resultReceiver:resultReceiver];
  }];
169

170
  [self setupNotificationCenterObservers];
C
Chinmay Garde 已提交
171

172
  [self connectToEngineAndLoad];
C
Chinmay Garde 已提交
173 174
}

175 176 177 178
- (void)setupNotificationCenterObservers {
  NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
  [center addObserver:self
             selector:@selector(onOrientationPreferencesUpdated:)
A
Adam Barth 已提交
179
                 name:@(shell::kOrientationUpdateNotificationName)
180 181
               object:nil];

182 183
  [center addObserver:self
             selector:@selector(onPreferredStatusBarStyleUpdated:)
A
Adam Barth 已提交
184
                 name:@(shell::kOverlayStyleUpdateNotificationName)
185 186
               object:nil];

187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
  [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];
211 212 213 214 215

  [center addObserver:self
             selector:@selector(onVoiceOverChanged:)
                 name:UIAccessibilityVoiceOverStatusChanged
               object:nil];
216 217 218 219 220

  [center addObserver:self
             selector:@selector(onMemoryWarning:)
                 name:UIApplicationDidReceiveMemoryWarningNotification
               object:nil];
C
Chinmay Garde 已提交
221 222
}

223
#pragma mark - Initializing the engine
224

225
- (void)alertView:(UIAlertView*)alertView
226
    clickedButtonAtIndex:(NSInteger)buttonIndex {
227 228 229
  exit(0);
}

230
- (void)connectToEngineAndLoad {
C
Chinmay Garde 已提交
231
  TRACE_EVENT0("flutter", "connectToEngineAndLoad");
232

233
  // We ask the VM to check what it supports.
234 235
  const enum VMType type =
      Dart_IsPrecompiledRuntime() ? VMTypePrecompilation : VMTypeInterpreter;
236

A
Adam Barth 已提交
237
  [_dartProject launchInEngine:&_platformView->engine()
238 239 240 241 242 243
                embedderVMType:type
                        result:^(BOOL success, NSString* message) {
                          if (!success) {
                            UIAlertView* alert = [[UIAlertView alloc]
                                    initWithTitle:@"Launch Error"
                                          message:message
244
                                         delegate:self
245 246 247 248 249 250
                                cancelButtonTitle:@"OK"
                                otherButtonTitles:nil];
                            [alert show];
                            [alert release];
                          }
                        }];
251 252 253 254 255
}

#pragma mark - Loading the view

- (void)loadView {
256
  FlutterView* view = [[FlutterView alloc] init];
257

258
  self.view = view;
259 260 261 262
  self.view.multipleTouchEnabled = YES;
  self.view.autoresizingMask =
      UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

263
  [view release];
264 265 266 267 268
}

#pragma mark - Application lifecycle notifications

- (void)applicationBecameActive:(NSNotification*)notification {
269
  [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.resumed"];
270 271 272
}

- (void)applicationWillResignActive:(NSNotification*)notification {
273
  [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.paused"];
274 275 276 277 278 279 280 281 282 283
}

#pragma mark - Touch event handling

enum MapperPhase {
  Accessed,
  Added,
  Removed,
};

284 285
using PointerChangeMapperPhase =
    std::pair<blink::PointerData::Change, MapperPhase>;
A
Adam Barth 已提交
286
static inline PointerChangeMapperPhase PointerChangePhaseFromUITouchPhase(
287 288 289
    UITouchPhase phase) {
  switch (phase) {
    case UITouchPhaseBegan:
A
Adam Barth 已提交
290 291
      return PointerChangeMapperPhase(blink::PointerData::Change::kDown,
                                      MapperPhase::Added);
292 293 294 295
    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 已提交
296 297
      return PointerChangeMapperPhase(blink::PointerData::Change::kMove,
                                      MapperPhase::Accessed);
298
    case UITouchPhaseEnded:
A
Adam Barth 已提交
299 300
      return PointerChangeMapperPhase(blink::PointerData::Change::kUp,
                                      MapperPhase::Removed);
301
    case UITouchPhaseCancelled:
A
Adam Barth 已提交
302 303
      return PointerChangeMapperPhase(blink::PointerData::Change::kCancel,
                                      MapperPhase::Removed);
304 305
  }

A
Adam Barth 已提交
306 307
  return PointerChangeMapperPhase(blink::PointerData::Change::kCancel,
                                  MapperPhase::Accessed);
308
}
C
Chinmay Garde 已提交
309 310

- (void)dispatchTouches:(NSSet*)touches phase:(UITouchPhase)phase {
311 312 313 314 315
  // 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 已提交
316
  auto eventTypePhase = PointerChangePhaseFromUITouchPhase(phase);
C
Chinmay Garde 已提交
317
  const CGFloat scale = [UIScreen mainScreen].scale;
A
Adam Barth 已提交
318
  auto packet = std::make_unique<blink::PointerDataPacket>(touches.count);
C
Chinmay Garde 已提交
319

A
Adam Barth 已提交
320
  int i = 0;
C
Chinmay Garde 已提交
321
  for (UITouch* touch in touches) {
322
    int device_id = 0;
323 324 325

    switch (eventTypePhase.second) {
      case Accessed:
326
        device_id = _touchMapper.identifierOf(touch);
327 328
        break;
      case Added:
329
        device_id = _touchMapper.registerTouch(touch);
330 331
        break;
      case Removed:
332
        device_id = _touchMapper.unregisterTouch(touch);
333 334
        break;
    }
335

336
    DCHECK(device_id != 0);
C
Chinmay Garde 已提交
337
    CGPoint windowCoordinates = [touch locationInView:nil];
338

A
Adam Barth 已提交
339 340 341
    blink::PointerData pointer_data;
    pointer_data.Clear();

342 343
    constexpr int kMicrosecondsPerSecond = 1000 * 1000;
    pointer_data.time_stamp = touch.timestamp * kMicrosecondsPerSecond;
A
Adam Barth 已提交
344 345
    pointer_data.change = eventTypePhase.first;
    pointer_data.kind = blink::PointerData::DeviceKind::kTouch;
346
    pointer_data.device = device_id;
A
Adam Barth 已提交
347 348 349 350 351 352
    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 已提交
353
  }
354

A
Adam Barth 已提交
355
  blink::Threads::UI()->PostTask(ftl::MakeCopyable([
356 357 358 359 360
    engine = _platformView->engine().GetWeakPtr(), packet = std::move(packet)
  ] {
    if (engine.get())
      engine->DispatchPointerDataPacket(*packet);
  }));
C
Chinmay Garde 已提交
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
}

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

379
#pragma mark - Handle view resizing
380

381 382
- (void)updateViewportMetrics {
  blink::Threads::UI()->PostTask([
383
    weak_platform_view = _platformView->GetWeakPtr(), metrics = _viewportMetrics
384
  ] {
385 386 387 388 389
    if (!weak_platform_view) {
      return;
    }
    weak_platform_view->UpdateSurfaceSize();
    weak_platform_view->engine().SetViewportMetrics(metrics);
390 391 392
  });
}

393
- (CGFloat)statusBarPadding {
394
  UIScreen* screen = self.view.window.screen;
395 396 397
  CGRect statusFrame = [UIApplication sharedApplication].statusBarFrame;
  CGRect viewFrame = [self.view convertRect:self.view.bounds
                          toCoordinateSpace:screen.coordinateSpace];
398 399
  CGRect intersection = CGRectIntersection(statusFrame, viewFrame);
  return CGRectIsNull(intersection) ? 0.0 : intersection.size.height;
400 401
}

402
- (void)viewDidLayoutSubviews {
403
  CGSize viewSize = self.view.bounds.size;
404 405
  CGFloat scale = [UIScreen mainScreen].scale;

406
  _viewportMetrics.device_pixel_ratio = scale;
407 408
  _viewportMetrics.physical_width = viewSize.width * scale;
  _viewportMetrics.physical_height = viewSize.height * scale;
409
  _viewportMetrics.physical_padding_top = [self statusBarPadding] * scale;
410
  [self updateViewportMetrics];
411 412
}

413
#pragma mark - Keyboard events
414

415 416 417 418 419
- (void)keyboardWasShown:(NSNotification*)notification {
  NSDictionary* info = [notification userInfo];
  CGFloat bottom = CGRectGetHeight(
      [[info objectForKey:UIKeyboardFrameBeginUserInfoKey] CGRectValue]);
  CGFloat scale = [UIScreen mainScreen].scale;
420 421
  _viewportMetrics.physical_padding_bottom = bottom * scale;
  [self updateViewportMetrics];
422 423
}

424
- (void)keyboardWillBeHidden:(NSNotification*)notification {
425 426
  _viewportMetrics.physical_padding_bottom = 0;
  [self updateViewportMetrics];
427 428
}

429 430 431
#pragma mark - Text input delegate

- (void)updateEditingClient:(int)client withState:(NSDictionary*)state {
432
  [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState" arguments:@[ @(client), state ]];
433 434
}

435 436 437 438 439 440 441
#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;

442
    NSNumber* update = info[@(shell::kOrientationUpdateNotificationKey)];
443 444 445 446 447 448 449 450 451 452 453 454

    if (update == nil) {
      return;
    }

    NSUInteger new_preferences = update.unsignedIntegerValue;

    if (new_preferences != _orientationPreferences) {
      _orientationPreferences = new_preferences;
      [UIViewController attemptRotationToDeviceOrientation];
    }
  });
455 456
}

457 458
- (BOOL)shouldAutorotate {
  return YES;
459 460
}

461 462 463 464
- (NSUInteger)supportedInterfaceOrientations {
  return _orientationPreferences;
}

465 466 467 468 469 470 471
#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.
472
  bool enabled = true;
473
#else
474
  bool enabled = UIAccessibilityIsVoiceOverRunning();
475
#endif
476
  _platformView->ToggleAccessibility(self.view, enabled);
477 478
}

479 480 481
#pragma mark - Memory Notifications

- (void)onMemoryWarning:(NSNotification*)notification {
482
  [_systemChannel.get() sendMessage:@{ @"type" : @"memoryPressure" }];
483 484
}

485 486 487 488 489 490
#pragma mark - Locale updates

- (void)onLocaleUpdated:(NSNotification*)notification {
  NSLocale* currentLocale = [NSLocale currentLocale];
  NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode];
  NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
491
  [_localizationChannel.get() invokeMethod:@"setLocale" arguments: @[ languageCode, countryCode ]];
492 493 494 495 496
}

#pragma mark - Surface creation and teardown updates

- (void)surfaceUpdated:(BOOL)appeared {
497
  CHECK(_platformView != nullptr);
498 499

  if (appeared) {
500
    _platformView->NotifyCreated();
501
  } else {
502
    _platformView->NotifyDestroyed();
503 504 505
  }
}

506 507
- (void)viewDidAppear:(BOOL)animated {
  [self surfaceUpdated:YES];
508 509
  [self onLocaleUpdated:nil];
  [self onVoiceOverChanged:nil];
510

511
  [super viewDidAppear:animated];
512
}
C
Chinmay Garde 已提交
513

514 515
- (void)viewWillDisappear:(BOOL)animated {
  [self surfaceUpdated:NO];
516 517

  [super viewWillDisappear:animated];
518 519
}

C
Chinmay Garde 已提交
520
- (void)dealloc {
521
  [[NSNotificationCenter defaultCenter] removeObserver:self];
522 523
  [super dealloc];
}
524

525 526 527 528 529
#pragma mark - Status Bar touch event handling

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

530
- (void)handleStatusBarTouches:(UIEvent*)event {
531 532 533 534 535 536 537 538
  // 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.
539
  for (UITouch* touch in event.allTouches) {
540 541 542 543
    if (touch.phase == UITouchPhaseBegan && touch.tapCount > 0) {
      CGPoint windowLoc = [touch locationInView:nil];
      CGPoint screenLoc = [touch.window convertPoint:windowLoc toWindow:nil];
      if (CGRectContainsPoint(statusBarFrame, screenLoc)) {
544
        NSSet* statusbarTouches = [NSSet setWithObject:touch];
545 546 547 548 549 550 551 552
        [self dispatchTouches:statusbarTouches phase:UITouchPhaseBegan];
        [self dispatchTouches:statusbarTouches phase:UITouchPhaseEnded];
        return;
      }
    }
  }
}

553 554 555 556 557 558 559 560 561 562 563
#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;

564
    NSNumber* update = info[@(shell::kOverlayStyleUpdateNotificationKey)];
565 566 567 568 569 570 571 572 573 574 575 576 577 578

    if (update == nil) {
      return;
    }

    NSInteger style = update.integerValue;

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

579
#pragma mark - Application Messages
580

581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609
- (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 已提交
610
@end