FlutterViewController.mm 20.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 9
#include <memory>

#include "flutter/common/threads.h"
10 11
#include "flutter/fml/platform/darwin/scoped_block.h"
#include "flutter/fml/platform/darwin/scoped_nsobject.h"
12
#include "flutter/shell/platform/darwin/common/buffer_conversions.h"
13
#include "flutter/shell/platform/darwin/common/platform_mac.h"
14
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterCodecs.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
namespace {

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

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

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

 private:
44 45
  explicit PlatformMessageResponseDarwin(
      PlatformMessageResponseCallback callback)
46
      : callback_(callback, fml::OwnershipPolicy::Retain) {}
47

48
  fml::ScopedBlock<PlatformMessageResponseCallback> callback_;
49 50 51 52
};

}  // 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
  fml::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 69 70 71 72 73 74 75
  fml::scoped_nsprotocol<FlutterPlatformPlugin*> _platformPlugin;
  fml::scoped_nsprotocol<FlutterTextInputPlugin*> _textInputPlugin;
  fml::scoped_nsprotocol<FlutterMethodChannel*> _localizationChannel;
  fml::scoped_nsprotocol<FlutterMethodChannel*> _navigationChannel;
  fml::scoped_nsprotocol<FlutterMethodChannel*> _platformChannel;
  fml::scoped_nsprotocol<FlutterMethodChannel*> _textInputChannel;
  fml::scoped_nsprotocol<FlutterMessageChannel*> _lifecycleChannel;
  fml::scoped_nsprotocol<FlutterMessageChannel*> _systemChannel;
76
  BOOL _initialized;
77 78
}

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

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

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

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

    [self performCommonViewControllerInitialization];
100
  }
101

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

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

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

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

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

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

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

128
  _localizationChannel.reset([[FlutterMethodChannel alloc]
129
         initWithName:@"flutter/localization"
130
      binaryMessenger:self
131
                codec:[FlutterJSONMethodCodec sharedInstance]]);
132 133

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

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

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

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

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

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

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

174
  [self setupNotificationCenterObservers];
C
Chinmay Garde 已提交
175

176
  [self connectToEngineAndLoad];
C
Chinmay Garde 已提交
177 178
}

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

186 187
  [center addObserver:self
             selector:@selector(onPreferredStatusBarStyleUpdated:)
A
Adam Barth 已提交
188
                 name:@(shell::kOverlayStyleUpdateNotificationName)
189 190
               object:nil];

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

  [center addObserver:self
             selector:@selector(onVoiceOverChanged:)
                 name:UIAccessibilityVoiceOverStatusChanged
               object:nil];
220 221 222 223 224

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

227
#pragma mark - Initializing the engine
228

229
- (void)alertView:(UIAlertView*)alertView
230
    clickedButtonAtIndex:(NSInteger)buttonIndex {
231 232 233
  exit(0);
}

234
- (void)connectToEngineAndLoad {
C
Chinmay Garde 已提交
235
  TRACE_EVENT0("flutter", "connectToEngineAndLoad");
236

237
  // We ask the VM to check what it supports.
238 239
  const enum VMType type =
      Dart_IsPrecompiledRuntime() ? VMTypePrecompilation : VMTypeInterpreter;
240

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

#pragma mark - Loading the view

- (void)loadView {
260
  FlutterView* view = [[FlutterView alloc] init];
261

262
  self.view = view;
263 264 265 266
  self.view.multipleTouchEnabled = YES;
  self.view.autoresizingMask =
      UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

267
  [view release];
268 269 270 271 272
}

#pragma mark - Application lifecycle notifications

- (void)applicationBecameActive:(NSNotification*)notification {
273
  [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.resumed"];
274 275 276
}

- (void)applicationWillResignActive:(NSNotification*)notification {
277
  [_lifecycleChannel.get() sendMessage:@"AppLifecycleState.paused"];
278 279 280 281 282 283 284 285 286 287
}

#pragma mark - Touch event handling

enum MapperPhase {
  Accessed,
  Added,
  Removed,
};

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

A
Adam Barth 已提交
310 311
  return PointerChangeMapperPhase(blink::PointerData::Change::kCancel,
                                  MapperPhase::Accessed);
312
}
C
Chinmay Garde 已提交
313 314

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

A
Adam Barth 已提交
324
  int i = 0;
C
Chinmay Garde 已提交
325
  for (UITouch* touch in touches) {
326
    int device_id = 0;
327 328 329

    switch (eventTypePhase.second) {
      case Accessed:
330
        device_id = _touchMapper.identifierOf(touch);
331 332
        break;
      case Added:
333
        device_id = _touchMapper.registerTouch(touch);
334 335
        break;
      case Removed:
336
        device_id = _touchMapper.unregisterTouch(touch);
337 338
        break;
    }
339

340
    FTL_DCHECK(device_id != 0);
C
Chinmay Garde 已提交
341
    CGPoint windowCoordinates = [touch locationInView:nil];
342

A
Adam Barth 已提交
343 344 345
    blink::PointerData pointer_data;
    pointer_data.Clear();

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

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

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

383
#pragma mark - Handle view resizing
384

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

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

406
- (void)viewDidLayoutSubviews {
407
  CGSize viewSize = self.view.bounds.size;
408 409
  CGFloat scale = [UIScreen mainScreen].scale;

410
  _viewportMetrics.device_pixel_ratio = scale;
411 412
  _viewportMetrics.physical_width = viewSize.width * scale;
  _viewportMetrics.physical_height = viewSize.height * scale;
413
  _viewportMetrics.physical_padding_top = [self statusBarPadding] * scale;
414
  [self updateViewportMetrics];
415 416
}

417
#pragma mark - Keyboard events
418

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

428
- (void)keyboardWillBeHidden:(NSNotification*)notification {
429 430
  _viewportMetrics.physical_padding_bottom = 0;
  [self updateViewportMetrics];
431 432
}

433 434 435
#pragma mark - Text input delegate

- (void)updateEditingClient:(int)client withState:(NSDictionary*)state {
436 437
  [_textInputChannel.get() invokeMethod:@"TextInputClient.updateEditingState"
                              arguments:@[ @(client), state ]];
438 439
}

440 441 442 443 444 445 446
#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;

447
    NSNumber* update = info[@(shell::kOrientationUpdateNotificationKey)];
448 449 450 451 452 453 454 455 456 457 458 459

    if (update == nil) {
      return;
    }

    NSUInteger new_preferences = update.unsignedIntegerValue;

    if (new_preferences != _orientationPreferences) {
      _orientationPreferences = new_preferences;
      [UIViewController attemptRotationToDeviceOrientation];
    }
  });
460 461
}

462 463
- (BOOL)shouldAutorotate {
  return YES;
464 465
}

466 467 468 469
- (NSUInteger)supportedInterfaceOrientations {
  return _orientationPreferences;
}

470 471 472 473 474 475 476
#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.
477
  bool enabled = true;
478
#else
479
  bool enabled = UIAccessibilityIsVoiceOverRunning();
480
#endif
481
  _platformView->ToggleAccessibility(self.view, enabled);
482 483
}

484 485 486
#pragma mark - Memory Notifications

- (void)onMemoryWarning:(NSNotification*)notification {
487
  [_systemChannel.get() sendMessage:@{ @"type" : @"memoryPressure" }];
488 489
}

490 491 492 493 494 495
#pragma mark - Locale updates

- (void)onLocaleUpdated:(NSNotification*)notification {
  NSLocale* currentLocale = [NSLocale currentLocale];
  NSString* languageCode = [currentLocale objectForKey:NSLocaleLanguageCode];
  NSString* countryCode = [currentLocale objectForKey:NSLocaleCountryCode];
496 497
  [_localizationChannel.get() invokeMethod:@"setLocale"
                                 arguments:@[ languageCode, countryCode ]];
498 499 500 501 502
}

#pragma mark - Surface creation and teardown updates

- (void)surfaceUpdated:(BOOL)appeared {
503
  FTL_CHECK(_platformView != nullptr);
504 505

  if (appeared) {
506
    _platformView->NotifyCreated();
507
  } else {
508
    _platformView->NotifyDestroyed();
509 510 511
  }
}

512 513
- (void)viewDidAppear:(BOOL)animated {
  [self surfaceUpdated:YES];
514 515
  [self onLocaleUpdated:nil];
  [self onVoiceOverChanged:nil];
516

517
  [super viewDidAppear:animated];
518
}
C
Chinmay Garde 已提交
519

520 521
- (void)viewWillDisappear:(BOOL)animated {
  [self surfaceUpdated:NO];
522 523

  [super viewWillDisappear:animated];
524 525
}

C
Chinmay Garde 已提交
526
- (void)dealloc {
527
  [[NSNotificationCenter defaultCenter] removeObserver:self];
528 529
  [super dealloc];
}
530

531 532 533 534 535
#pragma mark - Status Bar touch event handling

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

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

559 560 561 562 563 564 565 566 567 568 569
#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;

570
    NSNumber* update = info[@(shell::kOverlayStyleUpdateNotificationKey)];
571 572 573 574 575 576 577 578 579 580 581 582 583 584

    if (update == nil) {
      return;
    }

    NSInteger style = update.integerValue;

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

585
#pragma mark - Application Messages
586

587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
- (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 已提交
616
@end