提交 1d23f210 编写于 作者: A Adam Barth

Add support for app messages to iOS (#2651)

Fixes https://github.com/flutter/flutter/issues/3256
上级 86837edd
......@@ -251,11 +251,15 @@ if (is_android) {
sources = [
"platform/ios/framework/Headers/Flutter.h",
"platform/ios/framework/Headers/FlutterAppDelegate.h",
"platform/ios/framework/Headers/FlutterAsyncMessageListener.h",
"platform/ios/framework/Headers/FlutterDartProject.h",
"platform/ios/framework/Headers/FlutterMacros.h",
"platform/ios/framework/Headers/FlutterMessageListener.h",
"platform/ios/framework/Headers/FlutterViewController.h",
"platform/ios/framework/Source/accessibility_bridge.h",
"platform/ios/framework/Source/accessibility_bridge.mm",
"platform/ios/framework/Source/application_messages_impl.h",
"platform/ios/framework/Source/application_messages_impl.mm",
"platform/ios/framework/Source/FlutterAppDelegate.mm",
"platform/ios/framework/Source/FlutterDartProject.mm",
"platform/ios/framework/Source/FlutterDartProject_Internal.h",
......@@ -379,8 +383,10 @@ if (is_android) {
sources = [
"platform/ios/framework/Headers/Flutter.h",
"platform/ios/framework/Headers/FlutterAppDelegate.h",
"platform/ios/framework/Headers/FlutterAsyncMessageListener.h",
"platform/ios/framework/Headers/FlutterDartProject.h",
"platform/ios/framework/Headers/FlutterMacros.h",
"platform/ios/framework/Headers/FlutterMessageListener.h",
"platform/ios/framework/Headers/FlutterViewController.h",
]
outputs = [ "$framework_dir/Headers/{{source_file_part}}" ]
......
......@@ -5,7 +5,11 @@
#ifndef FLUTTER_FLUTTER_H_
#define FLUTTER_FLUTTER_H_
#include "FlutterAppDelegate.h"
#include "FlutterAsyncMessageListener.h"
#include "FlutterDartProject.h"
#include "FlutterMacros.h"
#include "FlutterMessageListener.h"
#include "FlutterViewController.h"
#endif // FLUTTER_FLUTTER_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_FLUTTERASYNCMESSAGELISTENER_H_
#define FLUTTER_FLUTTERASYNCMESSAGELISTENER_H_
#import <Foundation/Foundation.h>
#include "FlutterMacros.h"
FLUTTER_EXPORT
@protocol FlutterAsyncMessageListener<NSObject>
- (void)didReceiveString:(NSString*)message
callback:(void(^)(NSString*))sendResponse;
@end
#endif // FLUTTER_FLUTTERASYNCMESSAGELISTENER_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_FLUTTERMESSAGELISTENER_H_
#define FLUTTER_FLUTTERMESSAGELISTENER_H_
#import <Foundation/Foundation.h>
#include "FlutterMacros.h"
FLUTTER_EXPORT
@protocol FlutterMessageListener<NSObject>
- (NSString*)didReceiveString:(NSString*)message;
@end
#endif // FLUTTER_FLUTTERMESSAGELISTENER_H_
......@@ -5,11 +5,13 @@
#ifndef FLUTTER_FLUTTERVIEWCONTROLLER_H_
#define FLUTTER_FLUTTERVIEWCONTROLLER_H_
#include "FlutterMacros.h"
#include "FlutterDartProject.h"
#import <UIKit/UIKit.h>
#include "FlutterAsyncMessageListener.h"
#include "FlutterDartProject.h"
#include "FlutterMacros.h"
#include "FlutterMessageListener.h"
FLUTTER_EXPORT
@interface FlutterViewController : UIViewController
......@@ -18,6 +20,19 @@ FLUTTER_EXPORT
bundle:(NSBundle*)nibBundleOrNil
NS_DESIGNATED_INITIALIZER;
- (void)sendString:(NSString*)message
withMessageName:(NSString*)messageName;
- (void)sendString:(NSString*)message
withMessageName:(NSString*)messageName
callback:(void(^)(NSString*))callback;
- (void)setMessageListener:(NSObject<FlutterMessageListener>*)listener
forMessagesWithName:(NSString*)messageName;
- (void)setAsyncMessageListener:(NSObject<FlutterAsyncMessageListener>*)listener
forMessagesWithName:(NSString*)messageName;
@end
// Initializes Flutter for this process. Need only be called once per process.
......
......@@ -4,13 +4,20 @@
#import "sky/shell/platform/ios/framework/Headers/FlutterViewController.h"
#include "base/bind.h"
#include "base/mac/scoped_block.h"
#include "base/mac/scoped_nsautorelease_pool.h"
#include "base/mac/scoped_nsobject.h"
#include "base/strings/sys_string_conversions.h"
#include "base/trace_event/trace_event.h"
#include "mojo/public/cpp/application/connect.h"
#include "mojo/public/interfaces/application/service_provider.mojom.h"
#include "sky/engine/wtf/MakeUnique.h"
#include "sky/services/engine/sky_engine.mojom.h"
#include "sky/services/platform/app_messages.mojom.h"
#include "sky/services/platform/ios/system_chrome_impl.h"
#include "sky/services/semantics/semantics.mojom.h"
#include "sky/shell/platform/ios/framework/Source/application_messages_impl.h"
#include "sky/shell/platform/ios/framework/Source/flutter_touch_mapper.h"
#include "sky/shell/platform/ios/framework/Source/FlutterDartProject_Internal.h"
#include "sky/shell/platform/ios/framework/Source/FlutterDynamicServiceLoader.h"
......@@ -30,14 +37,16 @@ void FlutterInit(int argc, const char* argv[]) {
}
@implementation FlutterViewController {
FlutterDartProject* _dartProject;
base::scoped_nsprotocol<FlutterDartProject*> _dartProject;
UIInterfaceOrientationMask _orientationPreferences;
FlutterDynamicServiceLoader* _dynamicServiceLoader;
base::scoped_nsprotocol<FlutterDynamicServiceLoader*> _dynamicServiceLoader;
sky::ViewportMetricsPtr _viewportMetrics;
sky::shell::TouchMapper _touchMapper;
std::unique_ptr<sky::shell::ShellView> _shellView;
sky::SkyEnginePtr _engine;
mojo::ServiceProviderPtr _dartServices;
flutter::platform::ApplicationMessagesPtr _appMessageSender;
sky::shell::ApplicationMessagesImpl _appMessageReceiver;
BOOL _initialized;
}
......@@ -49,7 +58,7 @@ void FlutterInit(int argc, const char* argv[]) {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_dartProject = [project retain];
_dartProject.reset([project retain]);
[self performCommonViewControllerInitialization];
}
......@@ -74,7 +83,7 @@ void FlutterInit(int argc, const char* argv[]) {
_initialized = YES;
_orientationPreferences = UIInterfaceOrientationMaskAll;
_dynamicServiceLoader = [[FlutterDynamicServiceLoader alloc] init];
_dynamicServiceLoader.reset([[FlutterDynamicServiceLoader alloc] init]);
_viewportMetrics = sky::ViewportMetrics::New();
_shellView =
WTF::MakeUnique<sky::shell::ShellView>(sky::shell::Shell::Shared());
......@@ -160,6 +169,9 @@ void FlutterInit(int argc, const char* argv[]) {
[alert release];
}
}];
DCHECK(_dartServices);
mojo::ConnectToService(_dartServices.get(), &_appMessageSender);
}
static void DynamicServiceResolve(void* baton,
......@@ -178,13 +190,16 @@ static void DynamicServiceResolve(void* baton,
// the engine could outlive this controller
auto serviceResolutionCallback = base::Bind(
&DynamicServiceResolve,
base::Unretained(reinterpret_cast<void*>(_dynamicServiceLoader)));
base::Unretained(reinterpret_cast<void*>(_dynamicServiceLoader.get())));
new sky::shell::PlatformServiceProvider(serviceProviderProxy.Pass(),
serviceResolutionCallback);
mojo::ServiceProviderPtr viewServiceProvider;
new sky::shell::ViewServiceProvider(mojo::GetProxy(&viewServiceProvider));
new sky::shell::ViewServiceProvider(
base::Bind(&sky::shell::ApplicationMessagesImpl::AddBinding,
_appMessageReceiver.GetWeakPtr()),
mojo::GetProxy(&viewServiceProvider));
DCHECK(!_dartServices.is_bound());
sky::ServicesDataPtr services = sky::ServicesData::New();
......@@ -435,11 +450,47 @@ static inline PointerTypeMapperPhase PointerTypePhaseFromUITouchPhase(
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[super dealloc];
}
[_dynamicServiceLoader release];
[_dartProject release];
#pragma mark - Application Messages
[super dealloc];
- (void)sendString:(NSString*)message
withMessageName:(NSString*)messageName {
NSAssert(message, @"The message must not be null");
NSAssert(messageName, @"The messageName must not be null");
_appMessageSender->SendString(messageName.UTF8String, message.UTF8String,
[](const mojo::String& response) { });
}
- (void)sendString:(NSString*)message
withMessageName:(NSString*)messageName
callback:(void(^)(NSString*))callback {
NSAssert(message, @"The message must not be null");
NSAssert(messageName, @"The messageName must not be null");
NSAssert(callback, @"The callback must not be null");
base::mac::ScopedBlock<void(^)(NSString*)> callback_ptr(
callback, base::scoped_policy::RETAIN);
_appMessageSender->SendString(messageName.UTF8String, message.UTF8String,
[callback_ptr](const mojo::String& response) {
callback_ptr.get()(base::SysUTF8ToNSString(response));
});
}
- (void)setMessageListener:(NSObject<FlutterMessageListener>*)listener
forMessagesWithName:(NSString*)messageName {
NSAssert(listener, @"The listener must not be null");
NSAssert(messageName, @"The messageName must not be null");
_appMessageReceiver.SetMessageListener(messageName.UTF8String,
base::scoped_nsprotocol<NSObject<FlutterMessageListener>*>(listener));
}
- (void)setAsyncMessageListener:(NSObject<FlutterAsyncMessageListener>*)listener
forMessagesWithName:(NSString*)messageName {
NSAssert(listener, @"The listener must not be null");
NSAssert(messageName, @"The messageName must not be null");
_appMessageReceiver.SetAsyncMessageListener(messageName.UTF8String,
base::scoped_nsprotocol<NSObject<FlutterAsyncMessageListener>*>(listener));
}
@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.
#ifndef SKY_SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_APPLICATION_MESSAGES_IMPL_H_
#define SKY_SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_APPLICATION_MESSAGES_IMPL_H_
#include <unordered_map>
#include "base/memory/weak_ptr.h"
#include "base/mac/scoped_nsobject.h"
#include "mojo/common/binding_set.h"
#include "sky/services/platform/app_messages.mojom.h"
#include "sky/shell/platform/ios/framework/Headers/FlutterAsyncMessageListener.h"
#include "sky/shell/platform/ios/framework/Headers/FlutterMessageListener.h"
namespace sky {
namespace shell {
class ApplicationMessagesImpl : public flutter::platform::ApplicationMessages {
public:
ApplicationMessagesImpl();
~ApplicationMessagesImpl() override;
base::WeakPtr<ApplicationMessagesImpl> GetWeakPtr();
void AddBinding(mojo::InterfaceRequest<flutter::platform::ApplicationMessages> request);
void SetMessageListener(
const std::string& message_name,
base::scoped_nsprotocol<NSObject<FlutterMessageListener>*> listener);
void SetAsyncMessageListener(
const std::string& message_name,
base::scoped_nsprotocol<NSObject<FlutterAsyncMessageListener>*> listener);
private:
void SendString(const mojo::String& message_name,
const mojo::String& message,
const SendStringCallback& callback) override;
mojo::BindingSet<flutter::platform::ApplicationMessages> binding_;
std::unordered_map<
std::string,
base::scoped_nsprotocol<NSObject<FlutterMessageListener>*>> listeners_;
std::unordered_map<
std::string,
base::scoped_nsprotocol<NSObject<FlutterAsyncMessageListener>*>> async_listeners_;
base::WeakPtrFactory<ApplicationMessagesImpl> weak_factory_;
};
} // namespace shell
} // namespace sky
#endif // SKY_SHELL_PLATFORM_IOS_FRAMEWORK_SOURCE_ACCESSIBILITY_BRIDGE_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 "sky/shell/platform/ios/framework/Source/application_messages_impl.h"
#include "base/strings/sys_string_conversions.h"
namespace sky {
namespace shell {
ApplicationMessagesImpl::ApplicationMessagesImpl() : weak_factory_(this) {
}
ApplicationMessagesImpl::~ApplicationMessagesImpl() {
}
base::WeakPtr<ApplicationMessagesImpl> ApplicationMessagesImpl::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
void ApplicationMessagesImpl::AddBinding(
mojo::InterfaceRequest<flutter::platform::ApplicationMessages> request) {
binding_.AddBinding(this, request.Pass());
}
void ApplicationMessagesImpl::SetMessageListener(
const std::string& message_name,
base::scoped_nsprotocol<NSObject<FlutterMessageListener>*> listener) {
listeners_[message_name] = listener;
}
void ApplicationMessagesImpl::SetAsyncMessageListener(
const std::string& message_name,
base::scoped_nsprotocol<NSObject<FlutterAsyncMessageListener>*> listener) {
async_listeners_[message_name] = listener;
}
void ApplicationMessagesImpl::SendString(
const mojo::String& message_name,
const mojo::String& message,
const SendStringCallback& callback) {
std::string message_name_str = message_name;
NSString* ns_message = base::SysUTF8ToNSString(message);
{
auto it = listeners_.find(message_name_str);
if (it != listeners_.end()) {
NSString* response = [it->second didReceiveString:ns_message];
callback.Run(base::SysNSStringToUTF8(response));
return;
}
}
{
auto it = async_listeners_.find(message_name_str);
if (it != async_listeners_.end()) {
SendStringCallback local_callback = callback;
[it->second didReceiveString:ns_message callback:^(NSString* response){
local_callback.Run(base::SysNSStringToUTF8(response));
}];
}
}
}
} // namespace shell
} // namespace sky
......@@ -84,7 +84,8 @@ static inline pointer::PointerType EventTypeFromNSEventPhase(
base::Bind(DynamicServiceResolve));
mojo::ServiceProviderPtr view_service_provider;
new sky::shell::ViewServiceProvider(mojo::GetProxy(&view_service_provider));
new sky::shell::ViewServiceProvider(AppMesssagesConnector(),
mojo::GetProxy(&view_service_provider));
sky::ServicesDataPtr services = sky::ServicesData::New();
services->incoming_services = service_provider.Pass();
......
......@@ -9,14 +9,25 @@ namespace sky {
namespace shell {
ViewServiceProvider::ViewServiceProvider(
AppMesssagesConnector connect_to_app_messages,
mojo::InterfaceRequest<mojo::ServiceProvider> request)
: binding_(this, request.Pass()) {}
: binding_(this, request.Pass()),
connect_to_app_messages_(connect_to_app_messages) {
}
ViewServiceProvider::~ViewServiceProvider() {}
ViewServiceProvider::~ViewServiceProvider() {
}
void ViewServiceProvider::ConnectToService(
const mojo::String& service_name,
mojo::ScopedMessagePipeHandle client_handle) {
if (service_name == ::flutter::platform::ApplicationMessages::Name_ &&
!connect_to_app_messages_.is_null()) {
connect_to_app_messages_.Run(
mojo::MakeRequest<::flutter::platform::ApplicationMessages>(
client_handle.Pass()));
return;
}
#if TARGET_OS_IPHONE
if (service_name == ::editing::Keyboard::Name_) {
keyboard_.Create(
......
......@@ -9,6 +9,7 @@
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "mojo/public/interfaces/application/service_provider.mojom.h"
#include "sky/engine/wtf/Assertions.h"
#include "sky/services/platform/app_messages.mojom.h"
#if TARGET_OS_IPHONE
#include "sky/services/editing/ios/keyboard_impl.h"
......@@ -17,9 +18,14 @@
namespace sky {
namespace shell {
typedef base::Callback<
void(mojo::InterfaceRequest<flutter::platform::ApplicationMessages>)
> AppMesssagesConnector;
class ViewServiceProvider : public mojo::ServiceProvider {
public:
ViewServiceProvider(mojo::InterfaceRequest<mojo::ServiceProvider> request);
ViewServiceProvider(AppMesssagesConnector connect_to_app_messages,
mojo::InterfaceRequest<mojo::ServiceProvider> request);
~ViewServiceProvider() override;
void ConnectToService(const mojo::String& service_name,
......@@ -27,6 +33,7 @@ class ViewServiceProvider : public mojo::ServiceProvider {
private:
mojo::StrongBinding<mojo::ServiceProvider> binding_;
AppMesssagesConnector connect_to_app_messages_;
#if TARGET_OS_IPHONE
sky::services::editing::KeyboardFactory keyboard_;
#endif
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册