mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
Delegate AppDelegate life-cycle callbacks to plugins via separate object (flutter/engine#5173)
* Move the handling of delegating AppDelegate callback out of FlutterAppDelegate. Also moves the plugin registry to FlutterViewController. So each view-controller will handle its own plugins. This is intended to simplify including one or more Flutter views in an existing iOS app and giving more precise control of plugin registration. Fixes: https://github.com/flutter/flutter/issues/16539 * formatting * Update license golden file
This commit is contained in:
parent
9e5c5ac0b9
commit
f8d59eeabc
@ -22,6 +22,7 @@ _flutter_framework_headers = [
|
||||
"framework/Headers/FlutterMacros.h",
|
||||
"framework/Headers/FlutterNavigationController.h",
|
||||
"framework/Headers/FlutterPlugin.h",
|
||||
"framework/Headers/FlutterPluginAppLifeCycleDelegate.h",
|
||||
"framework/Headers/FlutterTexture.h",
|
||||
"framework/Headers/FlutterViewController.h",
|
||||
]
|
||||
@ -43,6 +44,7 @@ shared_library("create_flutter_framework_dylib") {
|
||||
"framework/Source/FlutterNavigationController.mm",
|
||||
"framework/Source/FlutterPlatformPlugin.h",
|
||||
"framework/Source/FlutterPlatformPlugin.mm",
|
||||
"framework/Source/FlutterPluginAppLifeCycleDelegate.mm",
|
||||
"framework/Source/FlutterStandardCodec.mm",
|
||||
"framework/Source/FlutterStandardCodec_Internal.h",
|
||||
"framework/Source/FlutterTextInputDelegate.h",
|
||||
|
||||
@ -43,6 +43,7 @@
|
||||
#include "FlutterMacros.h"
|
||||
#include "FlutterNavigationController.h"
|
||||
#include "FlutterPlugin.h"
|
||||
#include "FlutterPluginAppLifeCycleDelegate.h"
|
||||
#include "FlutterTexture.h"
|
||||
#include "FlutterViewController.h"
|
||||
|
||||
|
||||
@ -24,22 +24,10 @@
|
||||
* code as necessary from FlutterAppDelegate.mm.
|
||||
*/
|
||||
FLUTTER_EXPORT
|
||||
@interface FlutterAppDelegate : UIResponder<UIApplicationDelegate, FlutterPluginRegistry>
|
||||
@interface FlutterAppDelegate : UIResponder<UIApplicationDelegate, FlutterPluginRegistry, FlutterAppLifeCycleProvider>
|
||||
|
||||
@property(strong, nonatomic) UIWindow* window;
|
||||
|
||||
// Can be overriden by subclasses to provide a custom FlutterBinaryMessenger,
|
||||
// typically a FlutterViewController, for plugin interop.
|
||||
//
|
||||
// Defaults to window's rootViewController.
|
||||
- (NSObject<FlutterBinaryMessenger>*)binaryMessenger;
|
||||
|
||||
// Can be overriden by subclasses to provide a custom FlutterTextureRegistry,
|
||||
// typically a FlutterViewController, for plugin interop.
|
||||
//
|
||||
// Defaults to window's rootViewController.
|
||||
- (NSObject<FlutterTextureRegistry>*)textures;
|
||||
|
||||
@end
|
||||
|
||||
#endif // FLUTTER_FLUTTERDARTPROJECT_H_
|
||||
|
||||
@ -253,6 +253,14 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey;
|
||||
@end
|
||||
|
||||
/**
|
||||
Implement this in the `UIAppDelegate` of your app to enable Flutter plugins to register themselves to the application
|
||||
life cycle events.
|
||||
*/
|
||||
@protocol FlutterAppLifeCycleProvider
|
||||
- (void)addApplicationLifeCycleDelegate:(NSObject<FlutterPlugin>*)delegate;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END;
|
||||
|
||||
#endif // FLUTTER_FLUTTERPLUGIN_H_
|
||||
|
||||
@ -0,0 +1,135 @@
|
||||
// Copyright 2018 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_FLUTTERPLUGINAPPLIFECYCLEDELEGATE_H_
|
||||
#define FLUTTER_FLUTTERPLUGINAPPLIFECYCLEDELEGATE_H_
|
||||
|
||||
#include "FlutterPlugin.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
/**
|
||||
Propagates `UIAppDelegate` callbacks to registered plugins.
|
||||
*/
|
||||
FLUTTER_EXPORT
|
||||
@interface FlutterPluginAppLifeCycleDelegate : NSObject
|
||||
/**
|
||||
Registers `delegate` to receive life cycle callbacks via this FlutterPluginAppLifecycleDelegate as long as it is alive.
|
||||
|
||||
`delegate` will only referenced weakly.
|
||||
*/
|
||||
- (void)addDelegate:(NSObject<FlutterPlugin>*)delegate;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks.
|
||||
|
||||
- Returns: `NO` if any plugin vetoes application launch.
|
||||
*/
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks.
|
||||
*/
|
||||
- (void)applicationDidBecomeActive:(UIApplication*)application;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks.
|
||||
*/
|
||||
- (void)applicationWillResignActive:(UIApplication*)application;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks.
|
||||
*/
|
||||
- (void)applicationDidEnterBackground:(UIApplication*)application;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks.
|
||||
*/
|
||||
- (void)applicationWillEnterForeground:(UIApplication*)application;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks.
|
||||
*/
|
||||
- (void)applicationWillTerminate:(UIApplication*)application;
|
||||
|
||||
/**
|
||||
Called if this plugin has been registered for `UIApplicationDelegate` callbacks.
|
||||
*/
|
||||
- (void)application:(UIApplication*)application
|
||||
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks.
|
||||
*/
|
||||
- (void)application:(UIApplication*)application
|
||||
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks.
|
||||
*/
|
||||
- (void)application:(UIApplication*)application
|
||||
didReceiveRemoteNotification:(NSDictionary*)userInfo
|
||||
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
|
||||
the request.
|
||||
|
||||
- Returns: `YES` if any plugin handles the request.
|
||||
*/
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
openURL:(NSURL*)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
|
||||
the request.
|
||||
|
||||
- Returns: `YES` if any plugin handles the request.
|
||||
*/
|
||||
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
|
||||
the request.
|
||||
|
||||
- Returns: `YES` if any plugin handles the request.
|
||||
*/
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
openURL:(NSURL*)url
|
||||
sourceApplication:(NSString*)sourceApplication
|
||||
annotation:(id)annotation;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks.
|
||||
*/
|
||||
- (void)application:(UIApplication*)application
|
||||
performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
|
||||
completionHandler:(void (^)(BOOL succeeded))completionHandler
|
||||
API_AVAILABLE(ios(9.0));
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
|
||||
the request.
|
||||
|
||||
- Returns: `YES` if any plugin handles the request.
|
||||
*/
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
|
||||
completionHandler:(nonnull void (^)())completionHandler;
|
||||
|
||||
/**
|
||||
Calls all plugins registered for `UIApplicationDelegate` callbacks in order of registration until some plugin handles
|
||||
the request.
|
||||
|
||||
- Returns: `YES` if any plugin handles the request.
|
||||
*/
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
#endif // FLUTTER_FLUTTERPLUGINAPPLIFECYCLEDELEGATE_H_
|
||||
@ -11,10 +11,11 @@
|
||||
#include "FlutterBinaryMessenger.h"
|
||||
#include "FlutterDartProject.h"
|
||||
#include "FlutterMacros.h"
|
||||
#include "FlutterPlugin.h"
|
||||
#include "FlutterTexture.h"
|
||||
|
||||
FLUTTER_EXPORT
|
||||
@interface FlutterViewController : UIViewController<FlutterBinaryMessenger, FlutterTextureRegistry>
|
||||
@interface FlutterViewController : UIViewController<FlutterBinaryMessenger, FlutterTextureRegistry, FlutterPluginRegistry>
|
||||
|
||||
- (instancetype)initWithProject:(FlutterDartProject*)project
|
||||
nibName:(NSString*)nibNameOrNil
|
||||
@ -49,6 +50,8 @@ FLUTTER_EXPORT
|
||||
*/
|
||||
- (void)setInitialRoute:(NSString*)route;
|
||||
|
||||
- (id<FlutterPluginRegistry>)pluginRegistry;
|
||||
|
||||
@end
|
||||
|
||||
#endif // FLUTTER_FLUTTERVIEWCONTROLLER_H_
|
||||
|
||||
@ -3,46 +3,28 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterAppDelegate.h"
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterPluginAppLifeCycleDelegate.h"
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
|
||||
#include "lib/fxl/logging.h"
|
||||
|
||||
@interface FlutterAppDelegate ()
|
||||
@property(readonly, nonatomic) NSMutableArray* pluginDelegates;
|
||||
@property(readonly, nonatomic) NSMutableDictionary* pluginPublications;
|
||||
@end
|
||||
|
||||
@interface FlutterAppDelegateRegistrar : NSObject <FlutterPluginRegistrar>
|
||||
- (instancetype)initWithPlugin:(NSString*)pluginKey appDelegate:(FlutterAppDelegate*)delegate;
|
||||
@end
|
||||
|
||||
@implementation FlutterAppDelegate {
|
||||
UIBackgroundTaskIdentifier _debugBackgroundTask;
|
||||
FlutterPluginAppLifeCycleDelegate* _lifeCycleDelegate;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_pluginDelegates = [NSMutableArray new];
|
||||
_pluginPublications = [NSMutableDictionary new];
|
||||
_lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_pluginDelegates release];
|
||||
[_pluginPublications release];
|
||||
[_lifeCycleDelegate release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if (![plugin application:application didFinishLaunchingWithOptions:launchOptions]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
// Returns the key window's rootViewController, if it's a FlutterViewController.
|
||||
@ -65,257 +47,118 @@
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication*)application {
|
||||
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
|
||||
// The following keeps the Flutter session alive when the device screen locks
|
||||
// in debug mode. It allows continued use of features like hot reload and
|
||||
// taking screenshots once the device unlocks again.
|
||||
//
|
||||
// Note the name is not an identifier and multiple instances can exist.
|
||||
_debugBackgroundTask = [application
|
||||
beginBackgroundTaskWithName:@"Flutter debug task"
|
||||
expirationHandler:^{
|
||||
FXL_LOG(WARNING)
|
||||
<< "\nThe OS has terminated the Flutter debug connection for being "
|
||||
"inactive in the background for too long.\n\n"
|
||||
"There are no errors with your Flutter application.\n\n"
|
||||
"To reconnect, launch your application again via 'flutter run'";
|
||||
}];
|
||||
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin applicationDidEnterBackground:application];
|
||||
}
|
||||
}
|
||||
[_lifeCycleDelegate applicationDidEnterBackground:application];
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication*)application {
|
||||
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
|
||||
[application endBackgroundTask:_debugBackgroundTask];
|
||||
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin applicationWillEnterForeground:application];
|
||||
}
|
||||
}
|
||||
[_lifeCycleDelegate applicationWillEnterForeground:application];
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication*)application {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin applicationWillResignActive:application];
|
||||
}
|
||||
}
|
||||
[_lifeCycleDelegate applicationWillResignActive:application];
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication*)application {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin applicationDidBecomeActive:application];
|
||||
}
|
||||
}
|
||||
[_lifeCycleDelegate applicationDidBecomeActive:application];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication*)application {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin applicationWillTerminate:application];
|
||||
}
|
||||
}
|
||||
[_lifeCycleDelegate applicationWillTerminate:application];
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication*)application
|
||||
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin application:application didRegisterUserNotificationSettings:notificationSettings];
|
||||
}
|
||||
}
|
||||
[_lifeCycleDelegate application:application
|
||||
didRegisterUserNotificationSettings:notificationSettings];
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication*)application
|
||||
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
|
||||
}
|
||||
}
|
||||
[_lifeCycleDelegate application:application
|
||||
didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication*)application
|
||||
didReceiveRemoteNotification:(NSDictionary*)userInfo
|
||||
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application
|
||||
didReceiveRemoteNotification:userInfo
|
||||
fetchCompletionHandler:completionHandler]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
[_lifeCycleDelegate application:application
|
||||
didReceiveRemoteNotification:userInfo
|
||||
fetchCompletionHandler:completionHandler];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
openURL:(NSURL*)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application openURL:url options:options]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
return [_lifeCycleDelegate application:application openURL:url options:options];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application handleOpenURL:url]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
return [_lifeCycleDelegate application:application handleOpenURL:url];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
openURL:(NSURL*)url
|
||||
sourceApplication:(NSString*)sourceApplication
|
||||
annotation:(id)annotation {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application
|
||||
openURL:url
|
||||
sourceApplication:sourceApplication
|
||||
annotation:annotation]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
return [_lifeCycleDelegate application:application
|
||||
openURL:url
|
||||
sourceApplication:sourceApplication
|
||||
annotation:annotation];
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication*)application
|
||||
performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
|
||||
completionHandler:(void (^)(BOOL succeeded))completionHandler NS_AVAILABLE_IOS(9_0) {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application
|
||||
performActionForShortcutItem:shortcutItem
|
||||
completionHandler:completionHandler]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
[_lifeCycleDelegate application:application
|
||||
performActionForShortcutItem:shortcutItem
|
||||
completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication*)application
|
||||
handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
|
||||
completionHandler:(nonnull void (^)())completionHandler {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application
|
||||
handleEventsForBackgroundURLSession:identifier
|
||||
completionHandler:completionHandler]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
[_lifeCycleDelegate application:application
|
||||
handleEventsForBackgroundURLSession:identifier
|
||||
completionHandler:completionHandler];
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication*)application
|
||||
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application performFetchWithCompletionHandler:completionHandler]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
[_lifeCycleDelegate application:application performFetchWithCompletionHandler:completionHandler];
|
||||
}
|
||||
|
||||
// TODO(xster): move when doing https://github.com/flutter/flutter/issues/3671.
|
||||
- (NSObject<FlutterBinaryMessenger>*)binaryMessenger {
|
||||
UIViewController* rootViewController = _window.rootViewController;
|
||||
if ([rootViewController conformsToProtocol:@protocol(FlutterBinaryMessenger)]) {
|
||||
return (NSObject<FlutterBinaryMessenger>*)rootViewController;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSObject<FlutterTextureRegistry>*)textures {
|
||||
UIViewController* rootViewController = _window.rootViewController;
|
||||
if ([rootViewController conformsToProtocol:@protocol(FlutterTextureRegistry)]) {
|
||||
return (NSObject<FlutterTextureRegistry>*)rootViewController;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
#pragma mark - FlutterPluginRegistry methods. All delegating to the rootViewController
|
||||
|
||||
- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
|
||||
NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
|
||||
self.pluginPublications[pluginKey] = [NSNull null];
|
||||
return
|
||||
[[[FlutterAppDelegateRegistrar alloc] initWithPlugin:pluginKey appDelegate:self] autorelease];
|
||||
UIViewController* rootViewController = _window.rootViewController;
|
||||
if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
|
||||
return
|
||||
[[(FlutterViewController*)rootViewController pluginRegistry] registrarForPlugin:pluginKey];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)hasPlugin:(NSString*)pluginKey {
|
||||
return _pluginPublications[pluginKey] != nil;
|
||||
UIViewController* rootViewController = _window.rootViewController;
|
||||
if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
|
||||
return [[(FlutterViewController*)rootViewController pluginRegistry] hasPlugin:pluginKey];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
|
||||
return _pluginPublications[pluginKey];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation FlutterAppDelegateRegistrar {
|
||||
NSString* _pluginKey;
|
||||
FlutterAppDelegate* _appDelegate;
|
||||
UIViewController* rootViewController = _window.rootViewController;
|
||||
if ([rootViewController isKindOfClass:[FlutterViewController class]]) {
|
||||
return [[(FlutterViewController*)rootViewController pluginRegistry]
|
||||
valuePublishedByPlugin:pluginKey];
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (instancetype)initWithPlugin:(NSString*)pluginKey appDelegate:(FlutterAppDelegate*)appDelegate {
|
||||
self = [super init];
|
||||
NSAssert(self, @"Super init cannot be nil");
|
||||
_pluginKey = [pluginKey retain];
|
||||
_appDelegate = [appDelegate retain];
|
||||
return self;
|
||||
}
|
||||
#pragma mark - FlutterAppLifeCycleProvider methods
|
||||
|
||||
- (void)dealloc {
|
||||
[_pluginKey release];
|
||||
[_appDelegate release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSObject<FlutterBinaryMessenger>*)messenger {
|
||||
return [_appDelegate binaryMessenger];
|
||||
}
|
||||
|
||||
- (NSObject<FlutterTextureRegistry>*)textures {
|
||||
return [_appDelegate textures];
|
||||
}
|
||||
|
||||
- (void)publish:(NSObject*)value {
|
||||
_appDelegate.pluginPublications[_pluginKey] = value;
|
||||
}
|
||||
|
||||
- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
|
||||
channel:(FlutterMethodChannel*)channel {
|
||||
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
|
||||
[delegate handleMethodCall:call result:result];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate {
|
||||
[_appDelegate.pluginDelegates addObject:delegate];
|
||||
}
|
||||
|
||||
- (NSString*)lookupKeyForAsset:(NSString*)asset {
|
||||
return [FlutterDartProject lookupKeyForAsset:asset];
|
||||
}
|
||||
|
||||
- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
|
||||
return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
|
||||
- (void)addApplicationLifeCycleDelegate:(NSObject<FlutterPlugin>*)delegate {
|
||||
[_lifeCycleDelegate addDelegate:delegate];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@ -0,0 +1,278 @@
|
||||
// Copyright 2018 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/FlutterPluginAppLifeCycleDelegate.h"
|
||||
#include "flutter/shell/platform/darwin/ios/framework/Headers/FlutterViewController.h"
|
||||
#include "lib/fxl/logging.h"
|
||||
|
||||
@implementation FlutterPluginAppLifeCycleDelegate {
|
||||
UIBackgroundTaskIdentifier _debugBackgroundTask;
|
||||
|
||||
// Weak references to registered plugins.
|
||||
NSPointerArray* _pluginDelegates;
|
||||
}
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_pluginDelegates = [[NSPointerArray weakObjectsPointerArray] retain];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_pluginDelegates release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
static BOOL isPowerOfTwo(NSUInteger x) {
|
||||
return x != 0 && (x & (x - 1)) == 0;
|
||||
}
|
||||
|
||||
- (void)addDelegate:(NSObject<FlutterPlugin>*)delegate {
|
||||
[_pluginDelegates addPointer:(__bridge void*)delegate];
|
||||
if (isPowerOfTwo([_pluginDelegates count])) {
|
||||
[_pluginDelegates compact];
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
|
||||
for (id<FlutterPlugin> plugin in [_pluginDelegates allObjects]) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if (![plugin application:application didFinishLaunchingWithOptions:launchOptions]) {
|
||||
return NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Returns the key window's rootViewController, if it's a FlutterViewController.
|
||||
// Otherwise, returns nil.
|
||||
- (FlutterViewController*)rootFlutterViewController {
|
||||
UIViewController* viewController = [UIApplication sharedApplication].keyWindow.rootViewController;
|
||||
if ([viewController isKindOfClass:[FlutterViewController class]]) {
|
||||
return (FlutterViewController*)viewController;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication*)application {
|
||||
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
|
||||
// The following keeps the Flutter session alive when the device screen locks
|
||||
// in debug mode. It allows continued use of features like hot reload and
|
||||
// taking screenshots once the device unlocks again.
|
||||
//
|
||||
// Note the name is not an identifier and multiple instances can exist.
|
||||
_debugBackgroundTask = [application
|
||||
beginBackgroundTaskWithName:@"Flutter debug task"
|
||||
expirationHandler:^{
|
||||
FXL_LOG(WARNING)
|
||||
<< "\nThe OS has terminated the Flutter debug connection for being "
|
||||
"inactive in the background for too long.\n\n"
|
||||
"There are no errors with your Flutter application.\n\n"
|
||||
"To reconnect, launch your application again via 'flutter run'";
|
||||
}];
|
||||
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin applicationDidEnterBackground:application];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication*)application {
|
||||
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
|
||||
[application endBackgroundTask:_debugBackgroundTask];
|
||||
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin applicationWillEnterForeground:application];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication*)application {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin applicationWillResignActive:application];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication*)application {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin applicationDidBecomeActive:application];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication*)application {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin applicationWillTerminate:application];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication*)application
|
||||
didRegisterUserNotificationSettings:(UIUserNotificationSettings*)notificationSettings {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin application:application didRegisterUserNotificationSettings:notificationSettings];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication*)application
|
||||
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
[plugin application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication*)application
|
||||
didReceiveRemoteNotification:(NSDictionary*)userInfo
|
||||
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application
|
||||
didReceiveRemoteNotification:userInfo
|
||||
fetchCompletionHandler:completionHandler]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
openURL:(NSURL*)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey, id>*)options {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application openURL:url options:options]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication*)application handleOpenURL:(NSURL*)url {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application handleOpenURL:url]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
openURL:(NSURL*)url
|
||||
sourceApplication:(NSString*)sourceApplication
|
||||
annotation:(id)annotation {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application
|
||||
openURL:url
|
||||
sourceApplication:sourceApplication
|
||||
annotation:annotation]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void)application:(UIApplication*)application
|
||||
performActionForShortcutItem:(UIApplicationShortcutItem*)shortcutItem
|
||||
completionHandler:(void (^)(BOOL succeeded))completionHandler NS_AVAILABLE_IOS(9_0) {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application
|
||||
performActionForShortcutItem:shortcutItem
|
||||
completionHandler:completionHandler]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
handleEventsForBackgroundURLSession:(nonnull NSString*)identifier
|
||||
completionHandler:(nonnull void (^)())completionHandler {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application
|
||||
handleEventsForBackgroundURLSession:identifier
|
||||
completionHandler:completionHandler]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication*)application
|
||||
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler {
|
||||
for (id<FlutterPlugin> plugin in _pluginDelegates) {
|
||||
if (!plugin) {
|
||||
continue;
|
||||
}
|
||||
if ([plugin respondsToSelector:_cmd]) {
|
||||
if ([plugin application:application performFetchWithCompletionHandler:completionHandler]) {
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
@end
|
||||
@ -22,6 +22,12 @@
|
||||
#include "flutter/shell/platform/darwin/ios/platform_view_ios.h"
|
||||
|
||||
@interface FlutterViewController () <FlutterTextInputDelegate>
|
||||
@property(nonatomic, readonly) NSMutableDictionary* pluginPublications;
|
||||
@end
|
||||
|
||||
@interface FlutterViewControllerRegistrar : NSObject <FlutterPluginRegistrar>
|
||||
- (instancetype)initWithPlugin:(NSString*)pluginKey
|
||||
flutterViewController:(FlutterViewController*)flutterViewController;
|
||||
@end
|
||||
|
||||
@implementation FlutterViewController {
|
||||
@ -93,6 +99,8 @@
|
||||
if ([self setupShell]) {
|
||||
[self setupChannels];
|
||||
[self setupNotificationCenterObservers];
|
||||
|
||||
_pluginPublications = [NSMutableDictionary new];
|
||||
}
|
||||
}
|
||||
|
||||
@ -426,6 +434,7 @@
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
[_pluginPublications release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@ -971,4 +980,82 @@ constexpr CGFloat kStandardStatusBarHeight = 20.0;
|
||||
return [FlutterDartProject lookupKeyForAsset:asset fromPackage:package];
|
||||
}
|
||||
|
||||
- (id<FlutterPluginRegistry>)pluginRegistry {
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - FlutterPluginRegistry
|
||||
|
||||
- (NSObject<FlutterPluginRegistrar>*)registrarForPlugin:(NSString*)pluginKey {
|
||||
NSAssert(self.pluginPublications[pluginKey] == nil, @"Duplicate plugin key: %@", pluginKey);
|
||||
self.pluginPublications[pluginKey] = [NSNull null];
|
||||
return
|
||||
[[FlutterViewControllerRegistrar alloc] initWithPlugin:pluginKey flutterViewController:self];
|
||||
}
|
||||
|
||||
- (BOOL)hasPlugin:(NSString*)pluginKey {
|
||||
return _pluginPublications[pluginKey] != nil;
|
||||
}
|
||||
|
||||
- (NSObject*)valuePublishedByPlugin:(NSString*)pluginKey {
|
||||
return _pluginPublications[pluginKey];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation FlutterViewControllerRegistrar {
|
||||
NSString* _pluginKey;
|
||||
FlutterViewController* _flutterViewController;
|
||||
}
|
||||
|
||||
- (instancetype)initWithPlugin:(NSString*)pluginKey
|
||||
flutterViewController:(FlutterViewController*)flutterViewController {
|
||||
self = [super init];
|
||||
NSAssert(self, @"Super init cannot be nil");
|
||||
_pluginKey = [pluginKey retain];
|
||||
_flutterViewController = [flutterViewController retain];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[_pluginKey release];
|
||||
[_flutterViewController release];
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSObject<FlutterBinaryMessenger>*)messenger {
|
||||
return _flutterViewController;
|
||||
}
|
||||
|
||||
- (NSObject<FlutterTextureRegistry>*)textures {
|
||||
return _flutterViewController;
|
||||
}
|
||||
|
||||
- (void)publish:(NSObject*)value {
|
||||
_flutterViewController.pluginPublications[_pluginKey] = value;
|
||||
}
|
||||
|
||||
- (void)addMethodCallDelegate:(NSObject<FlutterPlugin>*)delegate
|
||||
channel:(FlutterMethodChannel*)channel {
|
||||
[channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) {
|
||||
[delegate handleMethodCall:call result:result];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate {
|
||||
id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
|
||||
if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) {
|
||||
id<FlutterAppLifeCycleProvider> lifeCycleProvider =
|
||||
(id<FlutterAppLifeCycleProvider>)appDelegate;
|
||||
[lifeCycleProvider addApplicationLifeCycleDelegate:delegate];
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString*)lookupKeyForAsset:(NSString*)asset {
|
||||
return [_flutterViewController lookupKeyForAsset:asset];
|
||||
}
|
||||
|
||||
- (NSString*)lookupKeyForAsset:(NSString*)asset fromPackage:(NSString*)package {
|
||||
return [_flutterViewController lookupKeyForAsset:asset fromPackage:package];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user