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 44 45 46 47
  void CompleteEmpty() override {
    ftl::RefPtr<PlatformMessageResponseDarwin> self(this);
    blink::Threads::Platform()->PostTask(
        ftl::MakeCopyable([ self ]() mutable {
          self->callback_.get()(nil);
        }));
  }
48 49

 private:
50 51
  explicit PlatformMessageResponseDarwin(
      PlatformMessageResponseCallback callback)
52
      : callback_(callback, fml::OwnershipPolicy::Retain) {}
53

54
  fml::ScopedBlock<PlatformMessageResponseCallback> callback_;
55 56 57 58
};

}  // namespace

59 60
@interface FlutterViewController ()<UIAlertViewDelegate,
                                    FlutterTextInputDelegate>
61 62
@end

63
@implementation FlutterViewController {
64
  fml::scoped_nsprotocol<FlutterDartProject*> _dartProject;
65
  UIInterfaceOrientationMask _orientationPreferences;
66
  UIStatusBarStyle _statusBarStyle;
67
  blink::ViewportMetrics _viewportMetrics;
68 69
  shell::TouchMapper _touchMapper;
  std::unique_ptr<shell::PlatformViewIOS> _platformView;
70 71 72 73 74 75 76 77
  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;
78
  BOOL _initialized;
79 80
}

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

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

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

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

    [self performCommonViewControllerInitialization];
102
  }
103

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

176
  [self setupNotificationCenterObservers];
C
Chinmay Garde 已提交
177

178
  [self connectToEngineAndLoad];
C
Chinmay Garde 已提交
179 180
}

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

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

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

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

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

229
#pragma mark - Initializing the engine
230

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

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

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

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

#pragma mark - Loading the view

- (void)loadView {
262
  FlutterView* view = [[FlutterView alloc] init];
263

264
  self.view = view;
265 266 267 268
  self.view.multipleTouchEnabled = YES;
  self.view.autoresizingMask =
      UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

269
  [view release];
270 271 272 273 274
}

#pragma mark - Application lifecycle notifications

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

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

#pragma mark - Touch event handling

enum MapperPhase {
  Accessed,
  Added,
  Removed,
};

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

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

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

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

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

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

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

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

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

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

385
#pragma mark - Handle view resizing
386

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

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

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

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

419
#pragma mark - Keyboard events
420

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

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

435 436 437
#pragma mark - Text input delegate

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

442 443 444 445 446 447 448
#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;

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

    if (update == nil) {
      return;
    }

    NSUInteger new_preferences = update.unsignedIntegerValue;

    if (new_preferences != _orientationPreferences) {
      _orientationPreferences = new_preferences;
      [UIViewController attemptRotationToDeviceOrientation];
    }
  });
462 463
}

464 465
- (BOOL)shouldAutorotate {
  return YES;
466 467
}

468 469 470 471
- (NSUInteger)supportedInterfaceOrientations {
  return _orientationPreferences;
}

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

486 487 488
#pragma mark - Memory Notifications

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

492 493 494 495 496 497
#pragma mark - Locale updates

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

#pragma mark - Surface creation and teardown updates

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

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

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

519
  [super viewDidAppear:animated];
520
}
C
Chinmay Garde 已提交
521

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

  [super viewWillDisappear:animated];
526 527
}

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

533 534 535 536 537
#pragma mark - Status Bar touch event handling

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

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

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

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

    if (update == nil) {
      return;
    }

    NSInteger style = update.integerValue;

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

587
#pragma mark - Application Messages
588

589
- (void)sendBinaryMessage:(NSData*)message channelName:(NSString*)channel {
590
  [self sendBinaryMessage:message channelName:channel binaryReplyHandler:nil];
591 592 593 594 595 596
}

- (void)sendBinaryMessage:(NSData*)message
              channelName:(NSString*)channel
       binaryReplyHandler:(FlutterBinaryReplyHandler)callback {
  NSAssert(channel, @"The channel must not be null");
597 598 599 600 601 602 603 604 605 606
  ftl::RefPtr<PlatformMessageResponseDarwin> response = (callback == nil)
    ? nullptr
    : ftl::MakeRefCounted<PlatformMessageResponseDarwin>(^(NSData* reply) {
         callback(reply);
      });
  ftl::RefPtr<blink::PlatformMessage> platformMessage = (message == nil)
    ? ftl::MakeRefCounted<blink::PlatformMessage>(channel.UTF8String, response)
    : ftl::MakeRefCounted<blink::PlatformMessage>(channel.UTF8String,
        shell::GetVectorFromNSData(message), response);
  _platformView->DispatchPlatformMessage(platformMessage);
607 608 609 610 611 612 613 614
}

- (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 已提交
615
@end