From 8b3f9b12411b83deced576c08e1f6c8d7f2c150d Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Wed, 4 Mar 2026 20:51:28 +0200 Subject: [PATCH] Initial support for UIScene --- .github/workflows/scripts-ios.yml | 31 +- .../nativeSources/CodenameOne_GLAppDelegate.h | 16 + .../nativeSources/CodenameOne_GLAppDelegate.m | 295 +++++++++++------- .../CodenameOne_GLSceneDelegate.h | 33 ++ .../CodenameOne_GLSceneDelegate.m | 94 ++++++ .../com/codename1/builders/IPhoneBuilder.java | 28 ++ scripts/build-ios-app.sh | 3 + .../common/codenameone_settings.properties | 1 + 8 files changed, 386 insertions(+), 115 deletions(-) create mode 100644 Ports/iOSPort/nativeSources/CodenameOne_GLSceneDelegate.h create mode 100644 Ports/iOSPort/nativeSources/CodenameOne_GLSceneDelegate.m diff --git a/.github/workflows/scripts-ios.yml b/.github/workflows/scripts-ios.yml index 073c7d4ac9..618e82b6cd 100644 --- a/.github/workflows/scripts-ios.yml +++ b/.github/workflows/scripts-ios.yml @@ -132,14 +132,39 @@ jobs: run: ./scripts/build-ios-port.sh -q -DskipTests timeout-minutes: 25 - - name: Build sample iOS app and compile workspace + - name: Build sample iOS app and compile workspace (UIScene on) + id: build-ios-app-scene + env: + IOS_UISCENE: "true" + run: ./scripts/build-ios-app.sh -q -DskipTests + timeout-minutes: 30 + + - name: Run iOS UI screenshot tests (UIScene on) + env: + ARTIFACTS_DIR: ${{ github.workspace }}/artifacts/ios-ui-tests-scene-true + run: | + set -euo pipefail + mkdir -p "${ARTIFACTS_DIR}" + + echo "workspace='${{ steps.build-ios-app-scene.outputs.workspace }}'" + echo "scheme='${{ steps.build-ios-app-scene.outputs.scheme }}'" + + ./scripts/run-ios-ui-tests.sh \ + "${{ steps.build-ios-app-scene.outputs.workspace }}" \ + "" \ + "${{ steps.build-ios-app-scene.outputs.scheme }}" + timeout-minutes: 30 + + - name: Build sample iOS app and compile workspace (UIScene off) id: build-ios-app + env: + IOS_UISCENE: "false" run: ./scripts/build-ios-app.sh -q -DskipTests timeout-minutes: 30 - - name: Run iOS UI screenshot tests + - name: Run iOS UI screenshot tests (UIScene off) env: - ARTIFACTS_DIR: ${{ github.workspace }}/artifacts + ARTIFACTS_DIR: ${{ github.workspace }}/artifacts/ios-ui-tests-scene-false run: | set -euo pipefail mkdir -p "${ARTIFACTS_DIR}" diff --git a/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.h b/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.h index 3ae2d62ddc..9c4cf4aab1 100644 --- a/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.h +++ b/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.h @@ -21,6 +21,9 @@ * need additional information or have any questions. */ #import +#ifdef CN1_USE_UI_SCENE +#import +#endif //#define CN1_INCLUDE_NOTIFICATIONS #ifdef CN1_INCLUDE_NOTIFICATIONS #import @@ -40,4 +43,17 @@ @property (nonatomic, retain) IBOutlet CodenameOne_GLViewController *viewController; +#ifdef CN1_USE_UI_SCENE +- (void)cn1InstallRootViewControllerIntoWindow:(UIWindow *)window; +- (void)cn1ApplicationWillResignActive; +- (void)cn1ApplicationDidEnterBackground; +- (void)cn1ApplicationWillEnterForeground; +- (void)cn1ApplicationDidBecomeActive; +- (BOOL)cn1ContinueUserActivity:(NSUserActivity *)userActivity; +- (BOOL)cn1OpenURL:(UIApplication *)application + url:(NSURL *)url + sourceApplication:(NSString *)sourceApplication + annotation:(id)annotation; +#endif + @end diff --git a/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.m b/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.m index f3b0042930..2374898b94 100644 --- a/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.m +++ b/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.m @@ -21,6 +21,9 @@ * need additional information or have any questions. */ #import "CodenameOne_GLAppDelegate.h" +#ifdef CN1_USE_UI_SCENE +#import "CodenameOne_GLSceneDelegate.h" +#endif #import "CN1JailbreakDetector.h" #include "xmlvm.h" #import "EAGLView.h" @@ -38,6 +41,8 @@ int pendingRemoteNotificationRegistrations = 0; BOOL isAppSuspended = NO; +static BOOL cn1GestureRecognizerInstalled = NO; +static BOOL cn1IsHiddenInBackground = NO; //GL_APP_DELEGATE_IMPORT //GL_APP_DELEGATE_INCLUDE @@ -111,6 +116,161 @@ @implementation CodenameOne_GLAppDelegate @synthesize viewController=_viewController; +- (CodenameOne_GLViewController *)cn1EnsureViewController +{ + if (self.viewController == nil) { +#ifdef CN1_USE_ARC + self.viewController = [[CodenameOne_GLViewController alloc] initWithNibName:@"CodenameOne_GLViewController" bundle:nil]; +#else + CodenameOne_GLViewController *viewController = [[CodenameOne_GLViewController alloc] initWithNibName:@"CodenameOne_GLViewController" bundle:nil]; + self.viewController = viewController; + [viewController release]; +#endif + } + return self.viewController; +} + +- (void)cn1InstallTapGestureRecognizerIfNeeded +{ + CodenameOne_GLViewController *viewController = [self cn1EnsureViewController]; + if (cn1GestureRecognizerInstalled || viewController.view.window == nil) { + return; + } + CN1TapGestureRecognizer* recognizer = [[CN1TapGestureRecognizer alloc] initWithTarget:nil action:nil]; + [recognizer install:viewController]; + [recognizer release]; + cn1GestureRecognizerInstalled = YES; +} + +- (void)cn1InstallRootViewControllerIntoWindow:(UIWindow *)window +{ + self.window = window; + self.window.rootViewController = [self cn1EnsureViewController]; + [self.window makeKeyAndVisible]; + [self cn1InstallTapGestureRecognizerIfNeeded]; +} + +- (void)cn1StoreAppArgForURL:(NSURL *)url +{ + if(url != nil) { + JAVA_OBJECT o = com_codename1_ui_Display_getInstance__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); + JAVA_OBJECT key = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG @"AppArg"); + JAVA_OBJECT value; + if([url isFileURL]) { + value = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG url.path); + } else { + value = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [url absoluteString]); + } + com_codename1_ui_Display_setProperty___java_lang_String_java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG o, key, value); + } +} + +- (BOOL)cn1ContinueUserActivity:(NSUserActivity *)userActivity +{ + if (userActivity != nil && [NSUserActivityTypeBrowsingWeb isEqualToString:userActivity.activityType] && userActivity.webpageURL != nil) { +#ifdef CN1_HANDLE_UNIVERSAL_LINKS + JAVA_OBJECT url = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [userActivity.webpageURL absoluteString]); + com_codename1_impl_ios_IOSImplementation_applicationReceivedUniversalLink___java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG url); +#else + JAVA_OBJECT launchUrlStr = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [userActivity.webpageURL absoluteString]); + JAVA_OBJECT appArgKey = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG @"AppArg"); + JAVA_OBJECT displayInstObj = com_codename1_ui_Display_getInstance__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); + com_codename1_ui_Display_setProperty___java_lang_String_java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG displayInstObj, appArgKey, launchUrlStr); +#endif + return YES; + } + return NO; +} + +- (void)cn1ApplicationWillResignActive +{ + com_codename1_impl_ios_IOSImplementation_applicationWillResignActive__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); +} + +- (void)cn1ApplicationDidEnterBackground +{ + #ifdef CN1_BLOCK_SCREENSHOTS_ON_ENTER_BACKGROUND + [[CodenameOne_GLViewController instance] eaglView].hidden = YES; + cn1IsHiddenInBackground = YES; +#endif + if(editingComponent != nil) { + [editingComponent resignFirstResponder]; + [editingComponent removeFromSuperview]; +#ifndef CN1_USE_ARC + [editingComponent release]; +#endif + editingComponent = nil; + } + com_codename1_impl_ios_IOSImplementation_applicationDidEnterBackground__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); + //----application_will_resign_active + isAppSuspended = YES; +#ifdef NEW_CODENAME_ONE_VM + java_lang_System_stopGC__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); + mallocWhileSuspended = 0; +#endif +} + +- (void)cn1ApplicationWillEnterForeground +{ + if (cn1IsHiddenInBackground) { + [[CodenameOne_GLViewController instance] eaglView].hidden = NO; + } + com_codename1_impl_ios_IOSImplementation_applicationWillEnterForeground__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); + CodenameOne_GLViewController* vc = [CodenameOne_GLViewController instance]; + if (vc != nil) { + [vc updateCanvas:YES]; + } +} + +- (void)cn1ApplicationDidBecomeActive +{ +#ifdef INCLUDE_CN1_PUSH + [UIApplication sharedApplication].applicationIconBadgeNumber = 0; +#endif + com_codename1_impl_ios_IOSImplementation_applicationDidBecomeActive__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); + //DELEGATE_applicationDidBecomeActive +} + +- (BOOL)cn1OpenURL:(UIApplication *)application url:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation +{ + JAVA_OBJECT str1 = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [url absoluteString]); + JAVA_OBJECT str2 = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG sourceApplication); + +#ifdef INCLUDE_GOOGLE_CONNECT +#ifndef GOOGLE_SIGNIN + BOOL res = [GPPURLHandler handleURL:url + sourceApplication:sourceApplication + annotation:annotation]; + if (res) { + return res; + } +#else + BOOL res = [[GIDSignIn sharedInstance] handleURL:url]; + if (res) { + return res; + } +#endif +#endif +#ifdef INCLUDE_FACEBOOK_CONNECT + BOOL fbRes = [[FBSDKApplicationDelegate sharedInstance] application:application + openURL:url + sourceApplication:sourceApplication + annotation:annotation]; + if (fbRes) { + return fbRes; + } +#endif + + //openURLMarkerEntry + +#ifdef NEW_CODENAME_ONE_VM + JAVA_BOOLEAN b = com_codename1_impl_ios_IOSImplementation_shouldApplicationHandleURL___java_lang_String_java_lang_String_R_boolean(CN1_THREAD_GET_STATE_PASS_ARG str1, str2); +#else + JAVA_BOOLEAN b = com_codename1_impl_ios_IOSImplementation_shouldApplicationHandleURL___java_lang_String_java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG str1, str2); +#endif + + return b; +} @@ -126,35 +286,19 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // Install signal handlers so that rather than the app crashing upon a BAD_ACCESS, the // app will throw an NPE. installSignalHandlers(); - self.window.rootViewController = self.viewController; - CN1TapGestureRecognizer* recognizer = [[CN1TapGestureRecognizer alloc] initWithTarget:nil action:nil]; - [recognizer install:self.viewController]; - [recognizer release]; + [self cn1EnsureViewController]; +#ifndef CN1_USE_UI_SCENE + [self cn1InstallRootViewControllerIntoWindow:self.window]; +#endif NSURL *url = (NSURL *)[launchOptions valueForKey:UIApplicationLaunchOptionsURLKey]; - if(url != nil) { - JAVA_OBJECT o = com_codename1_ui_Display_getInstance__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); - JAVA_OBJECT key = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG @"AppArg"); - JAVA_OBJECT value; - if([url isFileURL]) { - value = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG url.path); - } else { - value = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [url absoluteString]); - } - com_codename1_ui_Display_setProperty___java_lang_String_java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG o, key, value); - } + [self cn1StoreAppArgForURL:url]; if (@available(iOS 8, *)) { // App Links from associated domains NSDictionary *activityDictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsUserActivityDictionaryKey]; if (activityDictionary) { NSUserActivity *userActivity = [activityDictionary valueForKey:@"UIApplicationLaunchOptionsUserActivityKey"]; if (userActivity != nil) { - if ([NSUserActivityTypeBrowsingWeb isEqualToString:userActivity.activityType] && userActivity.webpageURL != nil) { - JAVA_OBJECT launchUrlStr = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [userActivity.webpageURL absoluteString]); - JAVA_OBJECT appArgKey = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG @"AppArg"); - JAVA_OBJECT displayInstObj = com_codename1_ui_Display_getInstance__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); - - com_codename1_ui_Display_setProperty___java_lang_String_java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG displayInstObj, appArgKey, launchUrlStr); - } + [self cn1ContinueUserActivity:userActivity]; } } } @@ -245,75 +389,32 @@ - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions: isAppSuspended = NO; if(launchOptions != nil) { NSURL *url = (NSURL *)[launchOptions valueForKey:UIApplicationLaunchOptionsURLKey]; - if(url != nil) { - JAVA_OBJECT o = com_codename1_ui_Display_getInstance__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); - JAVA_OBJECT key = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG @"AppArg"); - JAVA_OBJECT value; - if([url isFileURL]) { - value = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG url.path); - } else { - value = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [url absoluteString]); - } - com_codename1_ui_Display_setProperty___java_lang_String_java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG o, key, value); - } + [self cn1StoreAppArgForURL:url]; } return YES; } +#ifdef CN1_USE_UI_SCENE +- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options API_AVAILABLE(ios(13.0)) +{ + UISceneConfiguration *sceneConfiguration = [UISceneConfiguration configurationWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; + sceneConfiguration.delegateClass = [CodenameOne_GLSceneDelegate class]; + return sceneConfiguration; +} +#endif + #ifdef CN1_HANDLE_UNIVERSAL_LINKS // https://developer.apple.com/documentation/uikit/core_app/allowing_apps_and_websites_to_link_to_your_content?language=objc // https://github.com/codenameone/CodenameOne/issues/2677 - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *))restorationHandler { - if ([NSUserActivityTypeBrowsingWeb isEqualToString:userActivity.activityType] && userActivity.webpageURL != nil) { - JAVA_OBJECT url = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [userActivity.webpageURL absoluteString]); - com_codename1_impl_ios_IOSImplementation_applicationReceivedUniversalLink___java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG url); - return YES; - } - return NO; + return [self cn1ContinueUserActivity:userActivity]; } #endif - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { - JAVA_OBJECT str1 = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG [url absoluteString]); - JAVA_OBJECT str2 = fromNSString(CN1_THREAD_GET_STATE_PASS_ARG sourceApplication); - -#ifdef INCLUDE_GOOGLE_CONNECT -#ifndef GOOGLE_SIGNIN - // Handle Google Plus Login - BOOL res = [GPPURLHandler handleURL:url - sourceApplication:sourceApplication - annotation:annotation]; - if (res) { - return res; - } -#else - BOOL res = [[GIDSignIn sharedInstance] handleURL:url]; - if (res) { - return res; - } -#endif -#endif -#ifdef INCLUDE_FACEBOOK_CONNECT - BOOL fbRes = [[FBSDKApplicationDelegate sharedInstance] application:application - openURL:url - sourceApplication:sourceApplication - annotation:annotation]; - if (fbRes) { - return fbRes; - } -#endif - - //openURLMarkerEntry - -#ifdef NEW_CODENAME_ONE_VM - JAVA_BOOLEAN b = com_codename1_impl_ios_IOSImplementation_shouldApplicationHandleURL___java_lang_String_java_lang_String_R_boolean(CN1_THREAD_GET_STATE_PASS_ARG str1, str2); -#else - JAVA_BOOLEAN b = com_codename1_impl_ios_IOSImplementation_shouldApplicationHandleURL___java_lang_String_java_lang_String(CN1_THREAD_GET_STATE_PASS_ARG str1, str2); -#endif - - return b; + return [self cn1OpenURL:application url:url sourceApplication:sourceApplication annotation:annotation]; } - (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url @@ -327,63 +428,33 @@ - (void)applicationWillResignActive:(UIApplication *)application Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. */ - com_codename1_impl_ios_IOSImplementation_applicationWillResignActive__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); + [self cn1ApplicationWillResignActive]; //[self.viewController stopAnimation]; } -static BOOL cn1IsHiddenInBackground = NO; - (void)applicationDidEnterBackground:(UIApplication *)application { - #ifdef CN1_BLOCK_SCREENSHOTS_ON_ENTER_BACKGROUND - [[CodenameOne_GLViewController instance] eaglView].hidden = YES; - cn1IsHiddenInBackground = YES; -#endif - if(editingComponent != nil) { - [editingComponent resignFirstResponder]; - [editingComponent removeFromSuperview]; -#ifndef CN1_USE_ARC - [editingComponent release]; -#endif - editingComponent = nil; - } /* Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. */ - com_codename1_impl_ios_IOSImplementation_applicationDidEnterBackground__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); - //----application_will_resign_active - isAppSuspended = YES; -#ifdef NEW_CODENAME_ONE_VM - java_lang_System_stopGC__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); - mallocWhileSuspended = 0; -#endif + [self cn1ApplicationDidEnterBackground]; } - (void)applicationWillEnterForeground:(UIApplication *)application -{ if (cn1IsHiddenInBackground) { - [[CodenameOne_GLViewController instance] eaglView].hidden = NO; - } - +{ /* Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. */ - com_codename1_impl_ios_IOSImplementation_applicationWillEnterForeground__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); - CodenameOne_GLViewController* vc = [CodenameOne_GLViewController instance]; - if (vc != nil) { - [vc updateCanvas:YES]; - } + [self cn1ApplicationWillEnterForeground]; } - (void)applicationDidBecomeActive:(UIApplication *)application { -#ifdef INCLUDE_CN1_PUSH - [UIApplication sharedApplication].applicationIconBadgeNumber = 0; -#endif /* Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. */ //[self.viewController startAnimation]; - com_codename1_impl_ios_IOSImplementation_applicationDidBecomeActive__(CN1_THREAD_GET_STATE_PASS_SINGLE_ARG); - //DELEGATE_applicationDidBecomeActive + [self cn1ApplicationDidBecomeActive]; } - (void)applicationWillTerminate:(UIApplication *)application diff --git a/Ports/iOSPort/nativeSources/CodenameOne_GLSceneDelegate.h b/Ports/iOSPort/nativeSources/CodenameOne_GLSceneDelegate.h new file mode 100644 index 0000000000..7ce1739d38 --- /dev/null +++ b/Ports/iOSPort/nativeSources/CodenameOne_GLSceneDelegate.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +#import +#import "CodenameOne_GLAppDelegate.h" + +#ifdef CN1_USE_UI_SCENE +API_AVAILABLE(ios(13.0)) +@interface CodenameOne_GLSceneDelegate : UIResponder + +@property (nonatomic, retain) UIWindow *window; + +@end +#endif diff --git a/Ports/iOSPort/nativeSources/CodenameOne_GLSceneDelegate.m b/Ports/iOSPort/nativeSources/CodenameOne_GLSceneDelegate.m new file mode 100644 index 0000000000..27844def4c --- /dev/null +++ b/Ports/iOSPort/nativeSources/CodenameOne_GLSceneDelegate.m @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Codename One designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Codename One through http://www.codenameone.com/ if you + * need additional information or have any questions. + */ +#import "CodenameOne_GLSceneDelegate.h" + +#ifdef CN1_USE_UI_SCENE +@implementation CodenameOne_GLSceneDelegate + +@synthesize window=_window; + +- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions API_AVAILABLE(ios(13.0)) +{ + if (![scene isKindOfClass:[UIWindowScene class]]) { + return; + } + UIWindow *window = [[UIWindow alloc] initWithWindowScene:(UIWindowScene *)scene]; + CodenameOne_GLAppDelegate *appDelegate = (CodenameOne_GLAppDelegate *)[UIApplication sharedApplication].delegate; + [appDelegate cn1InstallRootViewControllerIntoWindow:window]; + self.window = window; +#ifndef CN1_USE_ARC + [window release]; +#endif + + UIOpenURLContext *urlContext = [connectionOptions.URLContexts anyObject]; + if (urlContext != nil) { + [appDelegate cn1OpenURL:[UIApplication sharedApplication] url:urlContext.URL sourceApplication:urlContext.options.sourceApplication annotation:urlContext.options.annotation]; + } + NSUserActivity *userActivity = [connectionOptions.userActivities anyObject]; + if (userActivity != nil) { + [appDelegate cn1ContinueUserActivity:userActivity]; + } +} + +- (void)sceneDidBecomeActive:(UIScene *)scene API_AVAILABLE(ios(13.0)) +{ + CodenameOne_GLAppDelegate *appDelegate = (CodenameOne_GLAppDelegate *)[UIApplication sharedApplication].delegate; + [appDelegate cn1ApplicationDidBecomeActive]; +} + +- (void)sceneWillResignActive:(UIScene *)scene API_AVAILABLE(ios(13.0)) +{ + CodenameOne_GLAppDelegate *appDelegate = (CodenameOne_GLAppDelegate *)[UIApplication sharedApplication].delegate; + [appDelegate cn1ApplicationWillResignActive]; +} + +- (void)sceneWillEnterForeground:(UIScene *)scene API_AVAILABLE(ios(13.0)) +{ + CodenameOne_GLAppDelegate *appDelegate = (CodenameOne_GLAppDelegate *)[UIApplication sharedApplication].delegate; + [appDelegate cn1ApplicationWillEnterForeground]; +} + +- (void)sceneDidEnterBackground:(UIScene *)scene API_AVAILABLE(ios(13.0)) +{ + CodenameOne_GLAppDelegate *appDelegate = (CodenameOne_GLAppDelegate *)[UIApplication sharedApplication].delegate; + [appDelegate cn1ApplicationDidEnterBackground]; +} + +- (void)scene:(UIScene *)scene openURLContexts:(NSSet *)URLContexts API_AVAILABLE(ios(13.0)) +{ + UIOpenURLContext *urlContext = [URLContexts anyObject]; + if (urlContext == nil) { + return; + } + CodenameOne_GLAppDelegate *appDelegate = (CodenameOne_GLAppDelegate *)[UIApplication sharedApplication].delegate; + [appDelegate cn1OpenURL:[UIApplication sharedApplication] url:urlContext.URL sourceApplication:urlContext.options.sourceApplication annotation:urlContext.options.annotation]; +} + +- (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity API_AVAILABLE(ios(13.0)) +{ + CodenameOne_GLAppDelegate *appDelegate = (CodenameOne_GLAppDelegate *)[UIApplication sharedApplication].delegate; + [appDelegate cn1ContinueUserActivity:userActivity]; +} + +@end +#endif diff --git a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java index 422a837cf4..a72d56bd7b 100644 --- a/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java +++ b/maven/codenameone-maven-plugin/src/main/java/com/codename1/builders/IPhoneBuilder.java @@ -719,6 +719,7 @@ public void usesClassMethod(String cls, String method) { } File glAppDelegate = new File(buildinRes, "CodenameOne_GLAppDelegate.m"); + boolean useUIScene = "true".equalsIgnoreCase(request.getArg("ios.uiscene", "false")); String integrateFacebook = ""; @@ -874,6 +875,14 @@ public void usesClassMethod(String cls, String method) { throw new BuildException("Failure while processing ios.blockScreenshotsOnEnterBackground build hint", ex); } } + + if (useUIScene) { + try { + replaceInFile(new File(buildinRes, "CodenameOne_GLAppDelegate.h"), "#ifdef CN1_USE_UI_SCENE", "#define CN1_USE_UI_SCENE\n#ifdef CN1_USE_UI_SCENE"); + } catch (IOException ex) { + throw new BuildException("Failure while processing ios.uiscene build hint", ex); + } + } String applicationDidEnterBackground = request.getArg("ios.applicationDidEnterBackground", null); if(applicationDidEnterBackground != null) { @@ -2355,6 +2364,25 @@ public boolean accept(File file, String string) { inject += "\nUILaunchStoryboardName"+request.getArg("ios.launchStoryboardName", "LaunchScreen")+""; } } + if ("true".equalsIgnoreCase(request.getArg("ios.uiscene", "false")) && !inject.contains("UIApplicationSceneManifest")) { + inject += "\nUIApplicationSceneManifest\n" + + "\n" + + " UIApplicationSupportsMultipleScenes\n" + + " \n" + + " UISceneConfigurations\n" + + " \n" + + " UIWindowSceneSessionRoleApplication\n" + + " \n" + + " \n" + + " UISceneConfigurationName\n" + + " Default Configuration\n" + + " UISceneDelegateClassName\n" + + " CodenameOne_GLSceneDelegate\n" + + " \n" + + " \n" + + " \n" + + ""; + } if(request.getArg("ios.fileSharingEnabled", "false").equals("true")) { inject += "\n UIFileSharingEnabled\n \n"; diff --git a/scripts/build-ios-app.sh b/scripts/build-ios-app.sh index b18451d73a..3a39638784 100755 --- a/scripts/build-ios-app.sh +++ b/scripts/build-ios-app.sh @@ -60,6 +60,8 @@ bia_log "Using JAVA_HOME at $JAVA_HOME" bia_log "Using JAVA17_HOME at $JAVA17_HOME" bia_log "Using Maven installation at $MAVEN_HOME" bia_log "Using CocoaPods version $(pod --version 2>/dev/null || echo '')" +IOS_UISCENE="${IOS_UISCENE:-false}" +bia_log "Building sample app with ios.uiscene=${IOS_UISCENE}" APP_DIR="scripts/hellocodenameone" @@ -78,6 +80,7 @@ export CN1_BUILD_STATS_FILE="$ARTIFACTS_DIR/iphone-builder-stats.txt" -DskipTests \ -Dcodename1.platform=ios \ -Dcodename1.buildTarget=ios-source \ + -Dcodename1.arg.ios.uiscene="${IOS_UISCENE}" \ -Dopen=false \ -U -e VM_END=$(date +%s) diff --git a/scripts/hellocodenameone/common/codenameone_settings.properties b/scripts/hellocodenameone/common/codenameone_settings.properties index b388059468..6e59a9a357 100644 --- a/scripts/hellocodenameone/common/codenameone_settings.properties +++ b/scripts/hellocodenameone/common/codenameone_settings.properties @@ -2,6 +2,7 @@ codename1.android.keystore= codename1.android.keystoreAlias= codename1.android.keystorePassword= codename1.arg.ios.newStorageLocation=true +codename1.arg.ios.uiscene=false codename1.arg.java.version=8 codename1.displayName=HelloCodenameOne codename1.icon=icon.png