Reland "ios: remove shared_application and support app extension build #44732" (flutter/engine#45351)

Relands https://github.com/flutter/engine/pull/44732 with fix. 

The original PR returns nil when the assets is not reachable, in some cases, the assets are not loaded yet but will be loaded later, so we should return the asset URL regardless. 

Also added a fallback to main bundle to match the previous implementation. 

The original PR was failed in internal tests in b/297654739
Now with the fix, all tests passed: cl/561449914

fixes https://github.com/flutter/flutter/issues/124289

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
This commit is contained in:
Chris Yang 2023-09-01 09:56:49 -07:00 committed by GitHub
parent 67c3c25948
commit b3fc48b91b
12 changed files with 211 additions and 58 deletions

View File

@ -9,12 +9,14 @@
NS_ASSUME_NONNULL_BEGIN
// Finds a bundle with the named `bundleID` within `searchURL`.
extern const NSString* kDefaultAssetPath;
// Finds a bundle with the named `flutterFrameworkBundleID` within `searchURL`.
//
// Returns `nil` if the bundle cannot be found or if errors are encountered.
NSBundle* FLTFrameworkBundleInternal(NSString* bundleID, NSURL* searchURL);
NSBundle* FLTFrameworkBundleInternal(NSString* flutterFrameworkBundleID, NSURL* searchURL);
// Finds a bundle with the named `bundleID`.
// Finds a bundle with the named `flutterFrameworkBundleID`.
//
// `+[NSBundle bundleWithIdentifier:]` is slow, and can take in the order of
// tens of milliseconds in a minimal flutter app, and closer to 100 milliseconds
@ -28,7 +30,25 @@ NSBundle* FLTFrameworkBundleInternal(NSString* bundleID, NSURL* searchURL);
// frameworks used by this file are placed. If the desired bundle cannot be
// found here, the implementation falls back to
// `+[NSBundle bundleWithIdentifier:]`.
NSBundle* FLTFrameworkBundleWithIdentifier(NSString* bundleID);
NSBundle* FLTFrameworkBundleWithIdentifier(NSString* flutterFrameworkBundleID);
// Finds the bundle of the application.
//
// Returns [NSBundle mainBundle] if the current running process is the application.
NSBundle* FLTGetApplicationBundle();
// Gets the flutter assets path directory from `bundle`.
//
// Returns `kDefaultAssetPath` if unable to find asset path from info.plist in `bundle`.
NSString* FLTAssetPath(NSBundle* bundle);
// Finds the Flutter asset directory from `bundle`.
//
// The raw path can be set by the application via info.plist's `FLTAssetsPath` key.
// If the key is not set, `flutter_assets` is used as the raw path value.
//
// If no valid asset is found under the raw path, returns nil.
NSURL* FLTAssetsURLFromBundle(NSBundle* bundle);
NS_ASSUME_NONNULL_END

View File

@ -8,7 +8,9 @@
FLUTTER_ASSERT_ARC
NSBundle* FLTFrameworkBundleInternal(NSString* bundleID, NSURL* searchURL) {
const NSString* kDefaultAssetPath = @"Frameworks/App.framework/flutter_assets";
NSBundle* FLTFrameworkBundleInternal(NSString* flutterFrameworkBundleID, NSURL* searchURL) {
NSDirectoryEnumerator<NSURL*>* frameworkEnumerator = [NSFileManager.defaultManager
enumeratorAtURL:searchURL
includingPropertiesForKeys:nil
@ -18,19 +20,49 @@ NSBundle* FLTFrameworkBundleInternal(NSString* bundleID, NSURL* searchURL) {
errorHandler:nil];
for (NSURL* candidate in frameworkEnumerator) {
NSBundle* bundle = [NSBundle bundleWithURL:candidate];
if ([bundle.bundleIdentifier isEqualToString:bundleID]) {
return bundle;
NSBundle* flutterFrameworkBundle = [NSBundle bundleWithURL:candidate];
if ([flutterFrameworkBundle.bundleIdentifier isEqualToString:flutterFrameworkBundleID]) {
return flutterFrameworkBundle;
}
}
return nil;
}
NSBundle* FLTFrameworkBundleWithIdentifier(NSString* bundleID) {
NSBundle* bundle = FLTFrameworkBundleInternal(bundleID, NSBundle.mainBundle.privateFrameworksURL);
if (bundle != nil) {
return bundle;
NSBundle* FLTGetApplicationBundle() {
NSBundle* mainBundle = [NSBundle mainBundle];
// App extension bundle is in <AppName>.app/PlugIns/Extension.appex.
if ([mainBundle.bundleURL.pathExtension isEqualToString:@"appex"]) {
// Up two levels.
return [NSBundle bundleWithURL:mainBundle.bundleURL.URLByDeletingLastPathComponent
.URLByDeletingLastPathComponent];
}
// Fallback to slow implementation.
return [NSBundle bundleWithIdentifier:bundleID];
return mainBundle;
}
NSBundle* FLTFrameworkBundleWithIdentifier(NSString* flutterFrameworkBundleID) {
NSBundle* appBundle = FLTGetApplicationBundle();
NSBundle* flutterFrameworkBundle =
FLTFrameworkBundleInternal(flutterFrameworkBundleID, appBundle.privateFrameworksURL);
if (flutterFrameworkBundle == nil) {
// Fallback to slow implementation.
flutterFrameworkBundle = [NSBundle bundleWithIdentifier:flutterFrameworkBundleID];
}
if (flutterFrameworkBundle == nil) {
flutterFrameworkBundle = [NSBundle mainBundle];
}
return flutterFrameworkBundle;
}
NSString* FLTAssetPath(NSBundle* bundle) {
return [bundle objectForInfoDictionaryKey:@"FLTAssetsPath"] ?: kDefaultAssetPath;
}
NSURL* FLTAssetsURLFromBundle(NSBundle* bundle) {
NSString* flutterAssetsPath = FLTAssetPath(bundle);
NSURL* assets = [bundle URLForResource:flutterAssetsPath withExtension:nil];
if (!assets) {
assets = [[NSBundle mainBundle] URLForResource:flutterAssetsPath withExtension:nil];
}
return assets;
}

View File

@ -348,6 +348,10 @@ shared_library("create_flutter_framework_dylib") {
ldflags = [ "-Wl,-install_name,@rpath/Flutter.framework/Flutter" ]
if (darwin_extension_safe) {
ldflags += [ "-fapplication-extension" ]
}
public = _flutter_framework_headers
deps = [
@ -438,7 +442,10 @@ copy("copy_license") {
shared_library("copy_and_verify_framework_module") {
framework_search_path = rebase_path("$root_out_dir")
visibility = [ ":*" ]
cflags_objc = [ "-F$framework_search_path" ]
cflags_objc = [
"-F$framework_search_path",
"-fapplication-extension",
]
sources = [ "framework/Source/FlutterUmbrellaImport.m" ]
deps = [
@ -446,6 +453,17 @@ shared_library("copy_and_verify_framework_module") {
":copy_framework_info_plist",
":copy_framework_module_map",
]
if (darwin_extension_safe) {
ldflags = [
"-F$framework_search_path",
"-fapplication-extension",
"-Xlinker",
"-fatal_warnings",
]
deps += [ ":copy_dylib" ]
frameworks = [ "Flutter.framework" ]
}
}
group("universal_flutter_framework") {

View File

@ -353,7 +353,8 @@ typedef enum {
*
* @param delegate The receiving object, such as the plugin's main class.
*/
- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate;
- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in plugins used in app extensions");
/**
* Returns the file name for the given asset.

View File

@ -41,16 +41,13 @@ flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle, NSProcessInfo* p
// 3. Settings from the NSBundle with the default bundle ID.
// 4. Settings from the main NSBundle and default values.
NSBundle* mainBundle = [NSBundle mainBundle];
NSBundle* mainBundle = FLTGetApplicationBundle();
NSBundle* engineBundle = [NSBundle bundleForClass:[FlutterViewController class]];
bool hasExplicitBundle = bundle != nil;
if (bundle == nil) {
bundle = FLTFrameworkBundleWithIdentifier([FlutterDartProject defaultBundleIdentifier]);
}
if (bundle == nil) {
bundle = mainBundle;
}
auto settings = flutter::SettingsFromCommandLine(command_line);
@ -122,29 +119,24 @@ flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle, NSProcessInfo* p
// Checks to see if the flutter assets directory is already present.
if (settings.assets_path.empty()) {
NSString* assetsName = [FlutterDartProject flutterAssetsName:bundle];
NSString* assetsPath = [bundle pathForResource:assetsName ofType:@""];
NSURL* assetsURL = FLTAssetsURLFromBundle(bundle);
if (assetsPath.length == 0) {
assetsPath = [mainBundle pathForResource:assetsName ofType:@""];
}
if (assetsPath.length == 0) {
NSLog(@"Failed to find assets path for \"%@\"", assetsName);
if (!assetsURL) {
NSLog(@"Failed to find assets path for \"%@\"", bundle);
} else {
settings.assets_path = assetsPath.UTF8String;
settings.assets_path = assetsURL.path.UTF8String;
// Check if there is an application kernel snapshot in the assets directory we could
// potentially use. Looking for the snapshot makes sense only if we have a VM that can use
// it.
if (!flutter::DartVM::IsRunningPrecompiledCode()) {
NSURL* applicationKernelSnapshotURL =
[NSURL URLWithString:@(kApplicationKernelSnapshotFileName)
relativeToURL:[NSURL fileURLWithPath:assetsPath]];
if ([[NSFileManager defaultManager] fileExistsAtPath:applicationKernelSnapshotURL.path]) {
[assetsURL URLByAppendingPathComponent:@(kApplicationKernelSnapshotFileName)];
NSError* error;
if ([applicationKernelSnapshotURL checkResourceIsReachableAndReturnError:&error]) {
settings.application_kernel_asset = applicationKernelSnapshotURL.path.UTF8String;
} else {
NSLog(@"Failed to find snapshot: %@", applicationKernelSnapshotURL.path);
NSLog(@"Failed to find snapshot at %@: %@", applicationKernelSnapshotURL.path, error);
}
}
}
@ -339,14 +331,7 @@ flutter::Settings FLTDefaultSettingsForBundle(NSBundle* bundle, NSProcessInfo* p
if (bundle == nil) {
bundle = FLTFrameworkBundleWithIdentifier([FlutterDartProject defaultBundleIdentifier]);
}
if (bundle == nil) {
bundle = [NSBundle mainBundle];
}
NSString* flutterAssetsName = [bundle objectForInfoDictionaryKey:@"FLTAssetsPath"];
if (flutterAssetsName == nil) {
flutterAssetsName = @"Frameworks/App.framework/flutter_assets";
}
return flutterAssetsName;
return FLTAssetPath(bundle);
}
+ (NSString*)domainNetworkPolicy:(NSDictionary*)appTransportSecurity {

View File

@ -73,6 +73,50 @@ FLUTTER_ASSERT_ARC
XCTAssertNotNil(found);
}
- (void)testFLTGetApplicationBundleWhenCurrentTargetIsNotExtension {
NSBundle* bundle = FLTGetApplicationBundle();
XCTAssertEqual(bundle, [NSBundle mainBundle]);
}
- (void)testFLTGetApplicationBundleWhenCurrentTargetIsExtension {
id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
NSURL* url = [[NSBundle mainBundle].bundleURL URLByAppendingPathComponent:@"foo/ext.appex"];
OCMStub([mockMainBundle bundleURL]).andReturn(url);
NSBundle* bundle = FLTGetApplicationBundle();
[mockMainBundle stopMocking];
XCTAssertEqualObjects(bundle.bundleURL, [NSBundle mainBundle].bundleURL);
}
- (void)testFLTAssetsURLFromBundle {
{
// Found asset path in info.plist (even not reachable)
id mockBundle = OCMClassMock([NSBundle class]);
OCMStub([mockBundle objectForInfoDictionaryKey:@"FLTAssetsPath"]).andReturn(@"foo/assets");
NSURL* mockAssetsURL = OCMClassMock([NSURL class]);
OCMStub([mockBundle URLForResource:@"foo/assets" withExtension:nil]).andReturn(mockAssetsURL);
OCMStub([mockAssetsURL checkResourceIsReachableAndReturnError:NULL]).andReturn(NO);
OCMStub([mockAssetsURL path]).andReturn(@"foo/assets");
NSURL* url = FLTAssetsURLFromBundle(mockBundle);
XCTAssertEqualObjects(url.path, @"foo/assets");
}
{
// No asset path in info.plist, defaults to main bundle
id mockBundle = OCMClassMock([NSBundle class]);
id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
NSURL* mockAssetsURL = OCMClassMock([NSURL class]);
OCMStub([mockBundle URLForResource:@"Frameworks/App.framework/flutter_assets"
withExtension:nil])
.andReturn(nil);
OCMStub([mockAssetsURL checkResourceIsReachableAndReturnError:NULL]).andReturn(NO);
OCMStub([mockAssetsURL path]).andReturn(@"path/to/foo/assets");
OCMStub([mockMainBundle URLForResource:@"Frameworks/App.framework/flutter_assets"
withExtension:nil])
.andReturn(mockAssetsURL);
NSURL* url = FLTAssetsURLFromBundle(mockBundle);
XCTAssertEqualObjects(url.path, @"path/to/foo/assets");
}
}
- (void)testDisableImpellerSettingIsCorrectlyParsed {
id mockMainBundle = OCMPartialMock([NSBundle mainBundle]);
OCMStub([mockMainBundle objectForInfoDictionaryKey:@"FLTEnableImpeller"]).andReturn(@"NO");

View File

@ -1515,7 +1515,8 @@ static void SetEntryPoint(flutter::Settings* settings, NSString* entrypoint, NSS
}];
}
- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate {
- (void)addApplicationDelegate:(NSObject<FlutterPlugin>*)delegate
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in plugins used in app extensions") {
id<UIApplicationDelegate> appDelegate = [[UIApplication sharedApplication] delegate];
if ([appDelegate conformsToProtocol:@protocol(FlutterAppLifeCycleProvider)]) {
id<FlutterAppLifeCycleProvider> lifeCycleProvider =

View File

@ -17,7 +17,10 @@ namespace {
constexpr char kTextPlainFormat[] = "text/plain";
const UInt32 kKeyPressClickSoundId = 1306;
#if not APPLICATION_EXTENSION_API_ONLY
const NSString* searchURLPrefix = @"x-web-search://?";
#endif
} // namespace
@ -37,6 +40,24 @@ const char* const kOverlayStyleUpdateNotificationKey =
using namespace flutter;
static void SetStatusBarHiddenForSharedApplication(BOOL hidden) {
#if APPLICATION_EXTENSION_API_ONLY
[UIApplication sharedApplication].statusBarHidden = hidden;
#else
FML_LOG(WARNING) << "Application based status bar styling is not available in app extension.";
#endif
}
static void SetStatusBarStyleForSharedApplication(UIStatusBarStyle style) {
#if APPLICATION_EXTENSION_API_ONLY
// Note: -[UIApplication setStatusBarStyle] is deprecated in iOS9
// in favor of delegating to the view controller.
[[UIApplication sharedApplication] setStatusBarStyle:style];
#else
FML_LOG(WARNING) << "Application based status bar styling is not available in app extension.";
#endif
}
@interface FlutterPlatformPlugin ()
/**
@ -141,6 +162,9 @@ using namespace flutter;
}
- (void)searchWeb:(NSString*)searchTerm {
#if APPLICATION_EXTENSION_API_ONLY
FML_LOG(WARNING) << "SearchWeb.invoke is not availabe in app extension.";
#else
NSString* escapedText = [searchTerm
stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet
URLHostAllowedCharacterSet]];
@ -149,6 +173,7 @@ using namespace flutter;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:searchURL]
options:@{}
completionHandler:nil];
#endif
}
- (void)playSystemSound:(NSString*)soundType {
@ -231,7 +256,7 @@ using namespace flutter;
// 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 = statusBarShouldBeHidden;
SetStatusBarHiddenForSharedApplication(statusBarShouldBeHidden);
}
}
@ -246,7 +271,7 @@ using namespace flutter;
// 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 = !edgeToEdge;
SetStatusBarHiddenForSharedApplication(!edgeToEdge);
}
[[NSNotificationCenter defaultCenter]
postNotificationName:edgeToEdge ? FlutterViewControllerShowHomeIndicator
@ -284,9 +309,7 @@ using namespace flutter;
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];
SetStatusBarStyleForSharedApplication(statusBarStyle);
}
}

View File

@ -50,9 +50,11 @@
FlutterResult result = ^(id result) {
OCMVerify([mockPlugin searchWeb:@"Testing Word!"]);
#if not APPLICATION_EXTENSION_API_ONLY
OCMVerify([mockApplication openURL:[NSURL URLWithString:@"x-web-search://?Testing%20Word!"]
options:@{}
completionHandler:nil]);
#endif
[invokeExpectation fulfill];
};
@ -82,9 +84,11 @@
FlutterResult result = ^(id result) {
OCMVerify([mockPlugin searchWeb:@"Test"]);
#if not APPLICATION_EXTENSION_API_ONLY
OCMVerify([mockApplication openURL:[NSURL URLWithString:@"x-web-search://?Test"]
options:@{}
completionHandler:nil]);
#endif
[invokeExpectation fulfill];
};

View File

@ -17,11 +17,16 @@ static const SEL kSelectorsHandledByPlugins[] = {
@selector(application:performFetchWithCompletionHandler:)};
@interface FlutterPluginAppLifeCycleDelegate ()
- (void)handleDidEnterBackground:(NSNotification*)notification;
- (void)handleWillEnterForeground:(NSNotification*)notification;
- (void)handleWillResignActive:(NSNotification*)notification;
- (void)handleDidBecomeActive:(NSNotification*)notification;
- (void)handleWillTerminate:(NSNotification*)notification;
- (void)handleDidEnterBackground:(NSNotification*)notification
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
- (void)handleWillEnterForeground:(NSNotification*)notification
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
- (void)handleWillResignActive:(NSNotification*)notification
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
- (void)handleDidBecomeActive:(NSNotification*)notification
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
- (void)handleWillTerminate:(NSNotification*)notification
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions");
@end
@implementation FlutterPluginAppLifeCycleDelegate {
@ -46,6 +51,7 @@ static const SEL kSelectorsHandledByPlugins[] = {
_notificationUnsubscribers = [[NSMutableArray alloc] init];
std::string cachePath = fml::paths::JoinPaths({getenv("HOME"), kCallbackCacheSubDir});
[FlutterCallbackCache setCachePath:[NSString stringWithUTF8String:cachePath.c_str()]];
#if not APPLICATION_EXTENSION_API_ONLY
[self addObserverFor:UIApplicationDidEnterBackgroundNotification
selector:@selector(handleDidEnterBackground:)];
[self addObserverFor:UIApplicationWillEnterForegroundNotification
@ -56,6 +62,7 @@ static const SEL kSelectorsHandledByPlugins[] = {
selector:@selector(handleDidBecomeActive:)];
[self addObserverFor:UIApplicationWillTerminateNotification
selector:@selector(handleWillTerminate:)];
#endif
_delegates = [[NSPointerArray weakObjectsPointerArray] retain];
_debugBackgroundTask = UIBackgroundTaskInvalid;
}
@ -134,7 +141,8 @@ static BOOL IsPowerOfTwo(NSUInteger x) {
return YES;
}
- (void)handleDidEnterBackground:(NSNotification*)notification {
- (void)handleDidEnterBackground:(NSNotification*)notification
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
UIApplication* application = [UIApplication sharedApplication];
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// The following keeps the Flutter session alive when the device screen locks
@ -166,7 +174,8 @@ static BOOL IsPowerOfTwo(NSUInteger x) {
}
}
- (void)handleWillEnterForeground:(NSNotification*)notification {
- (void)handleWillEnterForeground:(NSNotification*)notification
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
UIApplication* application = [UIApplication sharedApplication];
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
if (_debugBackgroundTask != UIBackgroundTaskInvalid) {
@ -184,7 +193,8 @@ static BOOL IsPowerOfTwo(NSUInteger x) {
}
}
- (void)handleWillResignActive:(NSNotification*)notification {
- (void)handleWillResignActive:(NSNotification*)notification
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
UIApplication* application = [UIApplication sharedApplication];
for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
if (!delegate) {
@ -196,7 +206,8 @@ static BOOL IsPowerOfTwo(NSUInteger x) {
}
}
- (void)handleDidBecomeActive:(NSNotification*)notification {
- (void)handleDidBecomeActive:(NSNotification*)notification
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
UIApplication* application = [UIApplication sharedApplication];
for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
if (!delegate) {
@ -208,7 +219,8 @@ static BOOL IsPowerOfTwo(NSUInteger x) {
}
}
- (void)handleWillTerminate:(NSNotification*)notification {
- (void)handleWillTerminate:(NSNotification*)notification
NS_EXTENSION_UNAVAILABLE_IOS("Disallowed in app extensions") {
UIApplication* application = [UIApplication sharedApplication];
for (NSObject<FlutterApplicationLifeCycleDelegate>* delegate in _delegates) {
if (!delegate) {

View File

@ -20,6 +20,7 @@ FLUTTER_ASSERT_ARC
XCTAssertNotNil(delegate);
}
#if not APPLICATION_EXTENSION_API_ONLY
- (void)testDidEnterBackground {
XCTNSNotificationExpectation* expectation = [[XCTNSNotificationExpectation alloc]
initWithName:UIApplicationDidEnterBackgroundNotification];
@ -88,5 +89,6 @@ FLUTTER_ASSERT_ARC
[self waitForExpectations:@[ expectation ] timeout:5.0];
OCMVerify([plugin applicationWillTerminate:[UIApplication sharedApplication]]);
}
#endif
@end

View File

@ -1973,7 +1973,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
#endif
[self requestGeometryUpdateForWindowScenes:scenes];
} else {
UIInterfaceOrientationMask currentInterfaceOrientation;
UIInterfaceOrientationMask currentInterfaceOrientation = 0;
if (@available(iOS 13.0, *)) {
UIWindowScene* windowScene = [self flutterWindowSceneIfViewLoaded];
if (!windowScene) {
@ -1983,7 +1983,13 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
}
currentInterfaceOrientation = 1 << windowScene.interfaceOrientation;
} else {
#if APPLICATION_EXTENSION_API_ONLY
FML_LOG(ERROR) << "Application based status bar orentiation update is not supported in "
"app extension. Orientation: "
<< currentInterfaceOrientation;
#else
currentInterfaceOrientation = 1 << [[UIApplication sharedApplication] statusBarOrientation];
#endif
}
if (!(_orientationPreferences & currentInterfaceOrientation)) {
[UIViewController attemptRotationToDeviceOrientation];
@ -2108,6 +2114,10 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
}
- (CGFloat)textScaleFactor {
#if APPLICATION_EXTENSION_API_ONLY
FML_LOG(WARNING) << "Dynamic content size update is not supported in app extension.";
return 1.0;
#else
UIContentSizeCategory category = [UIApplication sharedApplication].preferredContentSizeCategory;
// The delta is computed by approximating Apple's typography guidelines:
// https://developer.apple.com/ios/human-interface-guidelines/visual-design/typography/
@ -2158,6 +2168,7 @@ static flutter::PointerData::DeviceKind DeviceKindFromTouchType(UITouch* touch)
} else {
return 1.0;
}
#endif
}
- (BOOL)isAlwaysUse24HourFormat {