提交 8c3eeb57 编写于 作者: A Adam Barth 提交者: GitHub

Implement PlatformPlugin on iOS (#3125)

Also, expose a convenience class for processing JSON messages.
上级 8b911be0
......@@ -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",
......
// 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_
// 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
// 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
// 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];
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册