Implement PlatformPlugin on iOS (flutter/engine#3125)
Also, expose a convenience class for processing JSON messages.
This commit is contained in:
@@ -19,6 +19,7 @@ shared_library("flutter_framework_dylib") {
|
||||
"framework/Headers/FlutterAppDelegate.h",
|
||||
"framework/Headers/FlutterAsyncMessageListener.h",
|
||||
"framework/Headers/FlutterDartProject.h",
|
||||
"framework/Headers/FlutterJSONMessageListener.h",
|
||||
"framework/Headers/FlutterMacros.h",
|
||||
"framework/Headers/FlutterMessageListener.h",
|
||||
"framework/Headers/FlutterViewController.h",
|
||||
@@ -33,6 +34,9 @@ shared_library("flutter_framework_dylib") {
|
||||
"framework/Source/FlutterDartProject_Internal.h",
|
||||
"framework/Source/FlutterDartSource.h",
|
||||
"framework/Source/FlutterDartSource.mm",
|
||||
"framework/Source/FlutterJSONMessageListener.mm",
|
||||
"framework/Source/FlutterPlatformPlugin.h",
|
||||
"framework/Source/FlutterPlatformPlugin.mm",
|
||||
"framework/Source/FlutterView.h",
|
||||
"framework/Source/FlutterView.mm",
|
||||
"framework/Source/FlutterViewController.mm",
|
||||
@@ -129,6 +133,7 @@ copy("framework_headers") {
|
||||
"framework/Headers/FlutterAppDelegate.h",
|
||||
"framework/Headers/FlutterAsyncMessageListener.h",
|
||||
"framework/Headers/FlutterDartProject.h",
|
||||
"framework/Headers/FlutterJSONMessageListener.h",
|
||||
"framework/Headers/FlutterMacros.h",
|
||||
"framework/Headers/FlutterMessageListener.h",
|
||||
"framework/Headers/FlutterViewController.h",
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef FLUTTER_FLUTTERJSONMESSAGELISTENER_H_
|
||||
#define FLUTTER_FLUTTERJSONMESSAGELISTENER_H_
|
||||
|
||||
#include "FlutterMessageListener.h"
|
||||
|
||||
FLUTTER_EXPORT
|
||||
@interface FlutterJSONMessageListener : NSObject<FlutterMessageListener>
|
||||
|
||||
- (NSDictionary*)didReceiveJSON:(NSDictionary*)message;
|
||||
|
||||
@end
|
||||
|
||||
#endif // FLUTTER_FLUTTERJSONMESSAGELISTENER_H_
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterJSONMessageListener.h"
|
||||
|
||||
@implementation FlutterJSONMessageListener
|
||||
|
||||
- (NSString*)didReceiveString:(NSString*)message {
|
||||
NSError *error = nil;
|
||||
NSData* data = [message dataUsingEncoding:NSUTF8StringEncoding];
|
||||
NSDictionary* jsonObject = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
|
||||
if (error)
|
||||
return nil;
|
||||
NSDictionary* response = [self didReceiveJSON:jsonObject];
|
||||
if (!response)
|
||||
return nil;
|
||||
NSData* responseData = [NSJSONSerialization dataWithJSONObject:response options:0 error:nil];
|
||||
return [[[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding] autorelease];
|
||||
}
|
||||
|
||||
- (NSDictionary*)didReceiveJSON:(NSDictionary*)message {
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSString *)messageName {
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,9 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterJSONMessageListener.h"
|
||||
|
||||
@interface FlutterPlatformPlugin : FlutterJSONMessageListener
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,169 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h"
|
||||
|
||||
#include <AudioToolbox/AudioToolbox.h>
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <UIKit/UIApplication.h>
|
||||
#include <UIKit/UIKit.h>
|
||||
|
||||
static NSDictionary* GetDirectoryOfType(NSSearchPathDirectory dir) {
|
||||
NSArray* paths =
|
||||
NSSearchPathForDirectoriesInDomains(dir, NSUserDomainMask, YES);
|
||||
if (paths.count == 0)
|
||||
return nil;
|
||||
return @{ @"path": paths.firstObject };
|
||||
}
|
||||
|
||||
namespace flutter {
|
||||
namespace platform {
|
||||
|
||||
// TODO(abarth): Move these definitions from system_chrome_impl.cc to here.
|
||||
extern const char* const kOrientationUpdateNotificationName;
|
||||
extern const char* const kOrientationUpdateNotificationKey;
|
||||
extern const char* const kOverlayStyleUpdateNotificationName;
|
||||
extern const char* const kOverlayStyleUpdateNotificationKey;
|
||||
|
||||
} // namespace platform
|
||||
} // namespace flutter
|
||||
|
||||
using namespace flutter::platform;
|
||||
|
||||
@implementation FlutterPlatformPlugin
|
||||
|
||||
- (NSString *)messageName {
|
||||
return @"flutter/platform";
|
||||
}
|
||||
|
||||
- (NSDictionary*)didReceiveJSON:(NSDictionary*)message {
|
||||
NSString* method = message[@"method"];
|
||||
NSArray* args = message[@"args"];
|
||||
if ([method isEqualToString:@"SystemSound.play"]) {
|
||||
[self playSystemSound:args.firstObject];
|
||||
} else if ([method isEqualToString:@"HapticFeedback.vibrate"]) {
|
||||
[self vibrateHapticFeedback];
|
||||
} else if ([method isEqualToString:@"UrlLauncher.launch"]) {
|
||||
[self launchURL:args.firstObject];
|
||||
} else if ([method isEqualToString:@"SystemChrome.setPreferredOrientations"]) {
|
||||
[self setSystemChromePreferredOrientatations:args.firstObject];
|
||||
} else if ([method isEqualToString:@"SystemChrome.setApplicationSwitcherDescription"]) {
|
||||
[self setSystemChromeApplicationSwitcherDescription:args.firstObject];
|
||||
} else if ([method isEqualToString:@"SystemChrome.setEnabledSystemUIOverlays"]) {
|
||||
[self setSystemChromeEnabledSystemUIOverlays:args.firstObject];
|
||||
} else if ([method isEqualToString:@"SystemChrome.setSystemUIOverlayStyle"]) {
|
||||
[self setSystemChromeSystemUIOverlayStyle:args.firstObject];
|
||||
} else if ([method isEqualToString:@"PathProvider.getTemporaryDirectory"]) {
|
||||
return [self getPathProviderTemporaryDirectory];
|
||||
} else if ([method isEqualToString:@"PathProvider.getApplicationDocumentsDirectory"]) {
|
||||
return [self getPathProviderApplicationDocumentsDirectory];
|
||||
} else {
|
||||
// TODO(abarth): We should signal an error here that gets reported back to
|
||||
// Dart.
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)playSystemSound:(NSString*)soundType {
|
||||
if ([soundType isEqualToString:@"SystemSoundType.click"]) {
|
||||
// All feedback types are specific to Android and are treated as equal on
|
||||
// iOS. The surface must (and does) adopt the UIInputViewAudioFeedback
|
||||
// protocol
|
||||
[[UIDevice currentDevice] playInputClick];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)vibrateHapticFeedback {
|
||||
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate);
|
||||
}
|
||||
|
||||
- (NSDictionary*)launchURL:(NSString*)urlString {
|
||||
NSURL* url = [NSURL URLWithString:urlString];
|
||||
UIApplication* application = [UIApplication sharedApplication];
|
||||
bool success = [application canOpenURL:url] && [application openURL:url];
|
||||
return @{ @"succes": @(success) };
|
||||
}
|
||||
|
||||
- (void)setSystemChromePreferredOrientatations:(NSArray*)orientations {
|
||||
UIInterfaceOrientationMask mask = 0;
|
||||
|
||||
if (orientations.count == 0) {
|
||||
mask |= UIInterfaceOrientationMaskAll;
|
||||
} else {
|
||||
for (NSString* orientation in orientations) {
|
||||
if ([orientation isEqualToString:@"DeviceOrientation.portraitUp"])
|
||||
mask |= UIInterfaceOrientationMaskPortrait;
|
||||
else if ([orientation isEqualToString:@"DeviceOrientation.portraitDown"])
|
||||
mask |= UIInterfaceOrientationMaskPortraitUpsideDown;
|
||||
else if ([orientation isEqualToString:@"DeviceOrientation.landscapeLeft"])
|
||||
mask |= UIInterfaceOrientationMaskLandscapeLeft;
|
||||
else if ([orientation isEqualToString:@"DeviceOrientation.landscapeRight"])
|
||||
mask |= UIInterfaceOrientationMaskLandscapeRight;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mask)
|
||||
return;
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
postNotificationName:@(kOrientationUpdateNotificationName)
|
||||
object:nil
|
||||
userInfo:@{
|
||||
@(kOrientationUpdateNotificationKey) : @(mask)
|
||||
}];
|
||||
|
||||
}
|
||||
|
||||
- (void)setSystemChromeApplicationSwitcherDescription:(NSDictionary*)object {
|
||||
// No counterpart on iOS but is a benign operation. So no asserts.
|
||||
}
|
||||
|
||||
- (void)setSystemChromeEnabledSystemUIOverlays:(NSArray*)overlays {
|
||||
// Checks if the top status bar should be visible. This platform ignores all
|
||||
// other overlays
|
||||
|
||||
// We opt out of view controller based status bar visibility since we want
|
||||
// to be able to modify this on the fly. The key used is
|
||||
// UIViewControllerBasedStatusBarAppearance
|
||||
[UIApplication sharedApplication].statusBarHidden =
|
||||
![overlays containsObject:@"SystemUiOverlay.top"];
|
||||
}
|
||||
|
||||
- (void)setSystemChromeSystemUIOverlayStyle:(NSString*)style {
|
||||
UIStatusBarStyle statusBarStyle;
|
||||
if ([style isEqualToString:@"SystemUiOverlayStyle.light"])
|
||||
statusBarStyle = UIStatusBarStyleLightContent;
|
||||
else if ([style isEqualToString:@"SystemUiOverlayStyle.dark"])
|
||||
statusBarStyle = UIStatusBarStyleDefault;
|
||||
else
|
||||
return;
|
||||
|
||||
NSNumber* infoValue = [[NSBundle mainBundle]
|
||||
objectForInfoDictionaryKey:@"UIViewControllerBasedStatusBarAppearance"];
|
||||
Boolean delegateToViewController =
|
||||
(infoValue == nil || [infoValue boolValue]);
|
||||
|
||||
if (delegateToViewController) {
|
||||
// This notification is respected by the iOS embedder
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
postNotificationName:@(kOverlayStyleUpdateNotificationName)
|
||||
object:nil
|
||||
userInfo:@{
|
||||
@(kOverlayStyleUpdateNotificationKey) : @(statusBarStyle)
|
||||
}];
|
||||
} else {
|
||||
// Note: -[UIApplication setStatusBarStyle] is deprecated in iOS9
|
||||
// in favor of delegating to the view controller
|
||||
[[UIApplication sharedApplication] setStatusBarStyle:statusBarStyle];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSDictionary*)getPathProviderTemporaryDirectory {
|
||||
return GetDirectoryOfType(NSCachesDirectory);
|
||||
}
|
||||
|
||||
- (NSDictionary*)getPathProviderApplicationDocumentsDirectory {
|
||||
return GetDirectoryOfType(NSDocumentDirectory);
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "flutter/shell/gpu/gpu_surface_gl.h"
|
||||
#include "flutter/shell/platform/darwin/common/platform_mac.h"
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterDartProject_Internal.h"
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformPlugin.h"
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Source/flutter_touch_mapper.h"
|
||||
#include "flutter/shell/platform/darwin/ios/platform_view_ios.h"
|
||||
#include "lib/ftl/functional/make_copyable.h"
|
||||
@@ -36,6 +37,7 @@ void FlutterInit(int argc, const char* argv[]) {
|
||||
sky::ViewportMetricsPtr _viewportMetrics;
|
||||
shell::TouchMapper _touchMapper;
|
||||
std::unique_ptr<shell::PlatformViewIOS> _platformView;
|
||||
base::scoped_nsprotocol<FlutterPlatformPlugin*> _platformPlugin;
|
||||
|
||||
BOOL _initialized;
|
||||
}
|
||||
@@ -83,6 +85,9 @@ void FlutterInit(int argc, const char* argv[]) {
|
||||
reinterpret_cast<CAEAGLLayer*>(self.view.layer));
|
||||
_platformView->SetupResourceContextOnIOThread();
|
||||
|
||||
_platformPlugin.reset([[FlutterPlatformPlugin alloc] init]);
|
||||
[self addMessageListener:_platformPlugin.get()];
|
||||
|
||||
[self setupNotificationCenterObservers];
|
||||
|
||||
[self connectToEngineAndLoad];
|
||||
|
||||
Reference in New Issue
Block a user