From 0b987519d306475435320409d9f7002411795b63 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sat, 21 Feb 2026 18:32:19 +0200 Subject: [PATCH 1/8] Override previous local notifications on iOS --- Ports/iOSPort/nativeSources/IOSNative.m | 31 ++++++---- .../developer-guide/Working-With-iOS.asciidoc | 2 + .../LocalNotificationNative.java | 8 +++ .../tests/Cn1ssDeviceRunner.java | 1 + .../tests/LocalNotificationOverrideTest.java | 56 +++++++++++++++++++ .../LocalNotificationNativeImpl.java | 14 +++++ ...ocodenameone_LocalNotificationNativeImpl.h | 11 ++++ ...ocodenameone_LocalNotificationNativeImpl.m | 46 +++++++++++++++ 8 files changed, 158 insertions(+), 11 deletions(-) create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java create mode 100644 scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java create mode 100644 scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java create mode 100644 scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h create mode 100644 scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m diff --git a/Ports/iOSPort/nativeSources/IOSNative.m b/Ports/iOSPort/nativeSources/IOSNative.m index 1800226307..adc39439f1 100644 --- a/Ports/iOSPort/nativeSources/IOSNative.m +++ b/Ports/iOSPort/nativeSources/IOSNative.m @@ -9729,6 +9729,22 @@ JAVA_INT com_codename1_impl_ios_IOSNative_readNSFile___long(CN1_THREAD_STATE_MUL #endif +static void cn1CancelScheduledLocalNotificationById(NSString *nsId) { + if (nsId == nil) { + return; + } + UIApplication *app = [UIApplication sharedApplication]; + NSArray *eventArray = [app scheduledLocalNotifications]; + for (int i = 0; i < [eventArray count]; i++) { + UILocalNotification *n = [eventArray objectAtIndex:i]; + NSDictionary *userInfo = n.userInfo; + NSString *uid = [NSString stringWithFormat:@"%@", [userInfo valueForKey:@"__ios_id__"]]; + if ([nsId isEqualToString:uid]) { + [app cancelLocalNotification:n]; + } + } +} + JAVA_VOID com_codename1_impl_ios_IOSNative_sendLocalNotification___java_lang_String_java_lang_String_java_lang_String_java_lang_String_int_long_int_boolean( CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT me, JAVA_OBJECT notificationId, JAVA_OBJECT alertTitle, JAVA_OBJECT alertBody, JAVA_OBJECT alertSound, JAVA_INT badgeNumber, JAVA_LONG fireDate, JAVA_INT repeatType, JAVA_BOOLEAN foreground ) { @@ -9759,7 +9775,8 @@ JAVA_VOID com_codename1_impl_ios_IOSNative_sendLocalNotification___java_lang_Str body = tmpStr; NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; - [dict setObject: toNSString(CN1_THREAD_STATE_PASS_ARG notificationId) forKey: @"__ios_id__"]; + NSString *notificationIdString = toNSString(CN1_THREAD_STATE_PASS_ARG notificationId); + [dict setObject: notificationIdString forKey: @"__ios_id__"]; if (foreground) { [dict setObject: @"true" forKey: @"foreground"]; } @@ -9861,6 +9878,7 @@ JAVA_VOID com_codename1_impl_ios_IOSNative_sendLocalNotification___java_lang_Str dispatch_sync(dispatch_get_main_queue(), ^{ + cn1CancelScheduledLocalNotificationById(notificationIdString); #ifdef __IPHONE_8_0 if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]){ [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]]; @@ -9878,16 +9896,7 @@ JAVA_VOID com_codename1_impl_ios_IOSNative_cancelLocalNotification___java_lang_S } NSString *nsId = toNSString(CN1_THREAD_STATE_PASS_ARG notificationId); dispatch_sync(dispatch_get_main_queue(), ^{ - UIApplication *app = [UIApplication sharedApplication]; - NSArray *eventArray = [app scheduledLocalNotifications]; - for (int i=0; i<[eventArray count]; i++) { - UILocalNotification *n = [eventArray objectAtIndex:i]; - NSDictionary *userInfo = n.userInfo; - NSString *uid = [NSString stringWithFormat:@"%@", [userInfo valueForKey: @"__ios_id__"]]; - if ([nsId isEqualToString:uid]) { - [app cancelLocalNotification:n]; - } - } + cn1CancelScheduledLocalNotificationById(nsId); }); } diff --git a/docs/developer-guide/Working-With-iOS.asciidoc b/docs/developer-guide/Working-With-iOS.asciidoc index 6f6959558f..5b1ee8b232 100644 --- a/docs/developer-guide/Working-With-iOS.asciidoc +++ b/docs/developer-guide/Working-With-iOS.asciidoc @@ -105,6 +105,8 @@ Display.getInstance().scheduleLocalNotification( ); ----- +NOTE: On iOS, scheduling a local notification with an `id` that already exists now replaces the previously scheduled notification with that same `id` instead of keeping duplicates. + The resulting notification will look like .Resulting notification in iOS diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java new file mode 100644 index 0000000000..ee00c4bdd2 --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java @@ -0,0 +1,8 @@ +package com.codenameone.examples.hellocodenameone; + +import com.codename1.system.NativeInterface; + +public interface LocalNotificationNative extends NativeInterface { + void clearScheduledLocalNotifications(String notificationId); + int getScheduledLocalNotificationCount(String notificationId); +} diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java index 6157688bb8..60835b8fc4 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java @@ -75,6 +75,7 @@ public final class Cn1ssDeviceRunner extends DeviceRunner { new BackgroundThreadUiAccessTest(), new VPNDetectionAPITest(), new CallDetectionAPITest(), + new LocalNotificationOverrideTest(), new AccessibilityTest())); public static void addTest(BaseTest test) { diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java new file mode 100644 index 0000000000..b4ab24cf35 --- /dev/null +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java @@ -0,0 +1,56 @@ +package com.codenameone.examples.hellocodenameone.tests; + +import com.codename1.notifications.LocalNotification; +import com.codename1.system.NativeLookup; +import com.codename1.ui.Display; +import com.codenameone.examples.hellocodenameone.LocalNotificationNative; + +public class LocalNotificationOverrideTest extends BaseTest { + private static final long FIRE_OFFSET_MS = 5 * 60 * 1000L; + + @Override + public boolean runTest() { + LocalNotificationNative nativeInterface = NativeLookup.create(LocalNotificationNative.class); + if (nativeInterface == null || !nativeInterface.isSupported()) { + done(); + return true; + } + + try { + String notificationId = "cn1-local-notification-override-" + System.currentTimeMillis(); + nativeInterface.clearScheduledLocalNotifications(notificationId); + + schedule(notificationId, "first"); + schedule(notificationId, "second"); + + int count = nativeInterface.getScheduledLocalNotificationCount(notificationId); + nativeInterface.clearScheduledLocalNotifications(notificationId); + + if (count != 1) { + fail("Expected one scheduled notification for duplicate id, but found " + count); + return true; + } + done(); + } catch (Throwable t) { + fail("Local notification override test failed: " + t); + } + return true; + } + + private void schedule(String notificationId, String body) { + LocalNotification notification = new LocalNotification(); + notification.setId(notificationId); + notification.setAlertTitle("Local Notification Override Test"); + notification.setAlertBody(body); + Display.getInstance().scheduleLocalNotification( + notification, + System.currentTimeMillis() + FIRE_OFFSET_MS, + LocalNotification.REPEAT_NONE + ); + } + + @Override + public boolean shouldTakeScreenshot() { + return false; + } +} diff --git a/scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java b/scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java new file mode 100644 index 0000000000..c17812f3b4 --- /dev/null +++ b/scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java @@ -0,0 +1,14 @@ +package com.codenameone.examples.hellocodenameone; + +public class LocalNotificationNativeImpl { + public void clearScheduledLocalNotifications(String notificationId) { + } + + public int getScheduledLocalNotificationCount(String notificationId) { + return 0; + } + + public boolean isSupported() { + return false; + } +} diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h new file mode 100644 index 0000000000..10721d827c --- /dev/null +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h @@ -0,0 +1,11 @@ +#import +#import "CN1ObjcProxy.h" + +@interface com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl : NSObject { +} + +- (void)clearScheduledLocalNotifications:(NSString*)notificationId; +- (int)getScheduledLocalNotificationCount:(NSString*)notificationId; +- (BOOL)isSupported; + +@end diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m new file mode 100644 index 0000000000..08c38cb767 --- /dev/null +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m @@ -0,0 +1,46 @@ +#import "com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h" +#import + +@implementation com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl + +- (void)clearScheduledLocalNotifications:(NSString*)notificationId { + if (notificationId == nil) { + return; + } + dispatch_sync(dispatch_get_main_queue(), ^{ + UIApplication *app = [UIApplication sharedApplication]; + NSArray *scheduled = [app scheduledLocalNotifications]; + for (UILocalNotification *notification in scheduled) { + NSDictionary *userInfo = notification.userInfo; + NSString *uid = [NSString stringWithFormat:@"%@", [userInfo valueForKey:@"__ios_id__"]]; + if ([notificationId isEqualToString:uid]) { + [app cancelLocalNotification:notification]; + } + } + }); +} + +- (int)getScheduledLocalNotificationCount:(NSString*)notificationId { + if (notificationId == nil) { + return 0; + } + __block int count = 0; + dispatch_sync(dispatch_get_main_queue(), ^{ + UIApplication *app = [UIApplication sharedApplication]; + NSArray *scheduled = [app scheduledLocalNotifications]; + for (UILocalNotification *notification in scheduled) { + NSDictionary *userInfo = notification.userInfo; + NSString *uid = [NSString stringWithFormat:@"%@", [userInfo valueForKey:@"__ios_id__"]]; + if ([notificationId isEqualToString:uid]) { + count++; + } + } + }); + return count; +} + +- (BOOL)isSupported { + return YES; +} + +@end From 0276c8806bd641684442a15c82732e059c72fcf6 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sat, 21 Feb 2026 19:03:09 +0200 Subject: [PATCH 2/8] Fixed CI build issues --- .../LocalNotificationNativeImpl.java | 14 ++++++++++++++ ..._hellocodenameone_LocalNotificationNativeImpl.h | 1 - .../LocalNotificationNativeImpl.java | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java create mode 100644 scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java new file mode 100644 index 0000000000..c17812f3b4 --- /dev/null +++ b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java @@ -0,0 +1,14 @@ +package com.codenameone.examples.hellocodenameone; + +public class LocalNotificationNativeImpl { + public void clearScheduledLocalNotifications(String notificationId) { + } + + public int getScheduledLocalNotificationCount(String notificationId) { + return 0; + } + + public boolean isSupported() { + return false; + } +} diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h index 10721d827c..f50074a745 100644 --- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h @@ -1,5 +1,4 @@ #import -#import "CN1ObjcProxy.h" @interface com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl : NSObject { } diff --git a/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java b/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java new file mode 100644 index 0000000000..c17812f3b4 --- /dev/null +++ b/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java @@ -0,0 +1,14 @@ +package com.codenameone.examples.hellocodenameone; + +public class LocalNotificationNativeImpl { + public void clearScheduledLocalNotifications(String notificationId) { + } + + public int getScheduledLocalNotificationCount(String notificationId) { + return 0; + } + + public boolean isSupported() { + return false; + } +} From 8b04fc478f1edf78d232ab9930aef6bfe348dc61 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sat, 21 Feb 2026 22:15:10 +0200 Subject: [PATCH 3/8] Removed notification test --- .../examples/hellocodenameone/tests/Cn1ssDeviceRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java index 60835b8fc4..97cab90a87 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java @@ -75,7 +75,7 @@ public final class Cn1ssDeviceRunner extends DeviceRunner { new BackgroundThreadUiAccessTest(), new VPNDetectionAPITest(), new CallDetectionAPITest(), - new LocalNotificationOverrideTest(), + //new LocalNotificationOverrideTest(), new AccessibilityTest())); public static void addTest(BaseTest test) { From 28444dcca9131253ce1d3e7701029feaeb731811 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 22 Feb 2026 03:24:27 +0200 Subject: [PATCH 4/8] Removed native interface --- .../examples/hellocodenameone/LocalNotificationNative.java | 2 +- .../hellocodenameone/tests/LocalNotificationOverrideTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java index ee00c4bdd2..845a19dbcd 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java @@ -2,7 +2,7 @@ import com.codename1.system.NativeInterface; -public interface LocalNotificationNative extends NativeInterface { +public interface LocalNotificationNative {//extends NativeInterface { void clearScheduledLocalNotifications(String notificationId); int getScheduledLocalNotificationCount(String notificationId); } diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java index b4ab24cf35..0eb39f2984 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java @@ -10,7 +10,7 @@ public class LocalNotificationOverrideTest extends BaseTest { @Override public boolean runTest() { - LocalNotificationNative nativeInterface = NativeLookup.create(LocalNotificationNative.class); + /*LocalNotificationNative nativeInterface = NativeLookup.create(LocalNotificationNative.class); if (nativeInterface == null || !nativeInterface.isSupported()) { done(); return true; @@ -33,7 +33,7 @@ public boolean runTest() { done(); } catch (Throwable t) { fail("Local notification override test failed: " + t); - } + }*/ return true; } From 3b6fbcd1b18b2505c6635cb450fb1bc6db991063 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 22 Feb 2026 04:19:17 +0200 Subject: [PATCH 5/8] Removed wrong implementation of native interfaces --- .../hellocodenameone/LocalNotificationNative.java | 2 +- .../tests/LocalNotificationOverrideTest.java | 4 ++-- .../InPlaceEditViewNativeImpl.java | 10 ---------- .../LocalNotificationNativeImpl.java | 14 -------------- ..._hellocodenameone_LocalNotificationNativeImpl.m | 9 +++++---- 5 files changed, 8 insertions(+), 31 deletions(-) delete mode 100644 scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java delete mode 100644 scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java index 845a19dbcd..ee00c4bdd2 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNative.java @@ -2,7 +2,7 @@ import com.codename1.system.NativeInterface; -public interface LocalNotificationNative {//extends NativeInterface { +public interface LocalNotificationNative extends NativeInterface { void clearScheduledLocalNotifications(String notificationId); int getScheduledLocalNotificationCount(String notificationId); } diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java index 0eb39f2984..b4ab24cf35 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/LocalNotificationOverrideTest.java @@ -10,7 +10,7 @@ public class LocalNotificationOverrideTest extends BaseTest { @Override public boolean runTest() { - /*LocalNotificationNative nativeInterface = NativeLookup.create(LocalNotificationNative.class); + LocalNotificationNative nativeInterface = NativeLookup.create(LocalNotificationNative.class); if (nativeInterface == null || !nativeInterface.isSupported()) { done(); return true; @@ -33,7 +33,7 @@ public boolean runTest() { done(); } catch (Throwable t) { fail("Local notification override test failed: " + t); - }*/ + } return true; } diff --git a/scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java b/scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java deleted file mode 100644 index 8a6263700b..0000000000 --- a/scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.codenameone.examples.hellocodenameone; - -public class InPlaceEditViewNativeImpl { - public void runReproductionTest(ReproductionCallback callback) { - } - - public boolean isSupported() { - return false; - } -} diff --git a/scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java b/scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java deleted file mode 100644 index c17812f3b4..0000000000 --- a/scripts/hellocodenameone/ios/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.codenameone.examples.hellocodenameone; - -public class LocalNotificationNativeImpl { - public void clearScheduledLocalNotifications(String notificationId) { - } - - public int getScheduledLocalNotificationCount(String notificationId) { - return 0; - } - - public boolean isSupported() { - return false; - } -} diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m index 08c38cb767..de41f1160f 100644 --- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m @@ -4,7 +4,7 @@ @implementation com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl - (void)clearScheduledLocalNotifications:(NSString*)notificationId { - if (notificationId == nil) { + /*if (notificationId == nil) { return; } dispatch_sync(dispatch_get_main_queue(), ^{ @@ -17,11 +17,11 @@ - (void)clearScheduledLocalNotifications:(NSString*)notificationId { [app cancelLocalNotification:notification]; } } - }); + });*/ } - (int)getScheduledLocalNotificationCount:(NSString*)notificationId { - if (notificationId == nil) { + /*if (notificationId == nil) { return 0; } __block int count = 0; @@ -36,7 +36,8 @@ - (int)getScheduledLocalNotificationCount:(NSString*)notificationId { } } }); - return count; + return count;*/ + return 0; } - (BOOL)isSupported { From 5133267b55f7f379df7b87bdf2af1f2beb1b2bf6 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 22 Feb 2026 16:33:25 +0200 Subject: [PATCH 6/8] Fixed test issue and added skills --- Ports/iOSPort/nativeSources/IOSNative.m | 19 +++ .../LocalNotificationNativeImpl.java | 14 -- .../tests/Cn1ssDeviceRunner.java | 2 +- ...ocodenameone_LocalNotificationNativeImpl.h | 1 + ...ocodenameone_LocalNotificationNativeImpl.m | 9 +- .../LocalNotificationNativeImpl.java | 14 -- skills/ios-cn1ss-xcode-debug/SKILL.md | 133 ++++++++++++++++++ .../ios-cn1ss-xcode-debug/agents/openai.yaml | 4 + 8 files changed, 162 insertions(+), 34 deletions(-) delete mode 100644 scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java delete mode 100644 scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java create mode 100644 skills/ios-cn1ss-xcode-debug/SKILL.md create mode 100644 skills/ios-cn1ss-xcode-debug/agents/openai.yaml diff --git a/Ports/iOSPort/nativeSources/IOSNative.m b/Ports/iOSPort/nativeSources/IOSNative.m index adc39439f1..bf5ac2d4fd 100644 --- a/Ports/iOSPort/nativeSources/IOSNative.m +++ b/Ports/iOSPort/nativeSources/IOSNative.m @@ -5850,6 +5850,9 @@ void com_codename1_impl_ios_IOSNative_sendSMS___java_lang_String_java_lang_Strin void com_codename1_impl_ios_IOSNative_registerPush__(CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject) { #ifdef INCLUDE_CN1_PUSH2 dispatch_async(dispatch_get_main_queue(), ^{ +#if TARGET_OS_SIMULATOR + return; +#endif if (@available(iOS 10, *)) { // iOS 10 ObjC code pendingRemoteNotificationRegistrations++; @@ -9827,14 +9830,28 @@ JAVA_VOID com_codename1_impl_ios_IOSNative_sendLocalNotification___java_lang_Str authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; } +#if TARGET_OS_SIMULATOR + NSArray *ids = @[toNSString(CN1_THREAD_STATE_PASS_ARG notificationId)]; + [center removePendingNotificationRequestsWithIdentifiers:ids]; + [center removeDeliveredNotificationsWithIdentifiers:ids]; + [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { + if (error != nil) { + NSLog(@"%@", error.localizedDescription); + } + }]; +#else [center requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) { + NSArray *ids = @[toNSString(CN1_THREAD_STATE_PASS_ARG notificationId)]; + [center removePendingNotificationRequestsWithIdentifiers:ids]; + [center removeDeliveredNotificationsWithIdentifiers:ids]; [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { if (error != nil) { NSLog(@"%@", error.localizedDescription); } }]; }]; +#endif }); @@ -9880,9 +9897,11 @@ JAVA_VOID com_codename1_impl_ios_IOSNative_sendLocalNotification___java_lang_Str dispatch_sync(dispatch_get_main_queue(), ^{ cn1CancelScheduledLocalNotificationById(notificationIdString); #ifdef __IPHONE_8_0 +#if !TARGET_OS_SIMULATOR if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]){ [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]]; } +#endif #endif [[UIApplication sharedApplication] scheduleLocalNotification: notification]; }); diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java deleted file mode 100644 index c17812f3b4..0000000000 --- a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.codenameone.examples.hellocodenameone; - -public class LocalNotificationNativeImpl { - public void clearScheduledLocalNotifications(String notificationId) { - } - - public int getScheduledLocalNotificationCount(String notificationId) { - return 0; - } - - public boolean isSupported() { - return false; - } -} diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java index 97cab90a87..60835b8fc4 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/Cn1ssDeviceRunner.java @@ -75,7 +75,7 @@ public final class Cn1ssDeviceRunner extends DeviceRunner { new BackgroundThreadUiAccessTest(), new VPNDetectionAPITest(), new CallDetectionAPITest(), - //new LocalNotificationOverrideTest(), + new LocalNotificationOverrideTest(), new AccessibilityTest())); public static void addTest(BaseTest test) { diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h index f50074a745..10721d827c 100644 --- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h @@ -1,4 +1,5 @@ #import +#import "CN1ObjcProxy.h" @interface com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl : NSObject { } diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m index de41f1160f..08c38cb767 100644 --- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m @@ -4,7 +4,7 @@ @implementation com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl - (void)clearScheduledLocalNotifications:(NSString*)notificationId { - /*if (notificationId == nil) { + if (notificationId == nil) { return; } dispatch_sync(dispatch_get_main_queue(), ^{ @@ -17,11 +17,11 @@ - (void)clearScheduledLocalNotifications:(NSString*)notificationId { [app cancelLocalNotification:notification]; } } - });*/ + }); } - (int)getScheduledLocalNotificationCount:(NSString*)notificationId { - /*if (notificationId == nil) { + if (notificationId == nil) { return 0; } __block int count = 0; @@ -36,8 +36,7 @@ - (int)getScheduledLocalNotificationCount:(NSString*)notificationId { } } }); - return count;*/ - return 0; + return count; } - (BOOL)isSupported { diff --git a/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java b/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java deleted file mode 100644 index c17812f3b4..0000000000 --- a/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.codenameone.examples.hellocodenameone; - -public class LocalNotificationNativeImpl { - public void clearScheduledLocalNotifications(String notificationId) { - } - - public int getScheduledLocalNotificationCount(String notificationId) { - return 0; - } - - public boolean isSupported() { - return false; - } -} diff --git a/skills/ios-cn1ss-xcode-debug/SKILL.md b/skills/ios-cn1ss-xcode-debug/SKILL.md new file mode 100644 index 0000000000..493c899a38 --- /dev/null +++ b/skills/ios-cn1ss-xcode-debug/SKILL.md @@ -0,0 +1,133 @@ +--- +name: ios-cn1ss-xcode-debug +description: Reproduce and debug HelloCodenameOne iOS CN1SS build/test failures on a Mac using local ios-source generation plus xcodebuild logs. +--- + +# iOS CN1SS Xcode Debug + +Use this skill when CN1SS iOS runs fail, produce blank screenshots, or crash during build/test. +This workflow is validated to compile and run locally against repository snapshots. + +## Preconditions + +- macOS with Xcode CLI tools installed (`xcodebuild` available). +- Java 8 available locally. +- Repository root checked out. + +## Fast Workflow + +1. Set Java 8 for Maven runs. +2. Install CN1 iOS artifacts from this repo into `~/.m2`. +3. Force-regenerate iOS local source for `scripts/hellocodenameone` so local snapshots are actually copied. +4. Run `xcodebuild` on the generated workspace. +5. Run the full CN1SS runner and inspect screenshot/log artifacts. +6. Extract first compile/runtime/test failure from logs. + +## Commands + +Run from repo root unless noted. + +```bash +export JAVA_HOME=/Users/shai/Library/Java/JavaVirtualMachines/azul-1.8.0_372/Contents/Home +export PATH="$JAVA_HOME/bin:$PATH" +``` + +```bash +cd maven +mvn -q -pl ios -am -DskipTests -Dmaven.javadoc.skip=true install +``` + +```bash +cd ../scripts/hellocodenameone +rm -rf ios/target/hellocodenameone-ios-1.0-SNAPSHOT-ios-source +./mvnw -q package -DskipTests -Dcodename1.platform=ios -Dcodename1.buildTarget=ios-source -o -e +``` + +```bash +cd ios/target/hellocodenameone-ios-1.0-SNAPSHOT-ios-source +xcodebuild -list -workspace HelloCodenameOne.xcworkspace +xcodebuild -workspace HelloCodenameOne.xcworkspace -scheme HelloCodenameOne -configuration Debug -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' build +``` + +For full logs plus focused errors: + +```bash +xcodebuild -workspace HelloCodenameOne.xcworkspace -scheme HelloCodenameOne -configuration Debug -sdk iphonesimulator -destination 'generic/platform=iOS Simulator' build > /tmp/hellocn1-xcodebuild.log 2>&1 +rg -n "error:|fatal error:|undeclared function|BUILD FAILED|test host" /tmp/hellocn1-xcodebuild.log +``` + +Run the full UI test harness: + +```bash +XCODE_APP=/Applications/Xcode.app \ +scripts/run-ios-ui-tests.sh \ +scripts/hellocodenameone/ios/target/hellocodenameone-ios-1.0-SNAPSHOT-ios-source/HelloCodenameOne.xcworkspace \ +HelloCodenameOne +``` + +Post-run quick checks: + +```bash +ls -1 artifacts/*.png | wc -l +shasum artifacts/*.png | sort | uniq -c | sort -nr | head +rg -n "CN1SS:ERR|CN1SS:SUITE:FINISHED" artifacts/device-runner.log +``` + +## Common Failure Signatures + +- `Could not find test host for HelloCodenameOneTests` + - The generated unit-test scheme host path is invalid. Prefer building/running `HelloCodenameOne` scheme directly for CN1SS troubleshooting. + +- `call to undeclared function 'virtual_com_codename1_...'` + - Common cause: static invocation of an instance API in app/test code. + - Verify generated symbols in `HelloCodenameOne-src/com_codename1_*.m` and match call style. + - Reinstall local CN1 maven artifacts from current repo (`maven -pl ios -am`) and regenerate ios-source. + - Re-generate `ios-source` after reinstall. + +- Build succeeds but screenshots are blank or repetitive + - Check CN1SS log markers in app/test output: + - `CN1SS:INFO:test=... png_bytes=...` + - `CN1SS:ERR:...` + - If PNG bytes are tiny or absent, investigate app startup and current form readiness (`Cn1ssDeviceRunnerHelper.emitCurrentFormScreenshot` path). + - If many screenshots share the same hash, the runner is capturing a stable frame repeatedly. Check test navigation/state transitions before capture. + - If tests appear hung and simulator shows system permission alerts, this is usually not a screenshot pipeline bug; unblock/disable the modal first. + +- `Scheme file not found for env injection ... xcschemes/*.xcscheme` + - The runner can continue, but scheme-based env injection is skipped. + - Inspect `artifacts/device-runner.log` and decoded screenshots directly. + +## Artifacts To Inspect + +- Generated iOS project: + - `scripts/hellocodenameone/ios/target/hellocodenameone-ios-1.0-SNAPSHOT-ios-source` +- Build log: + - `/tmp/hellocn1-xcodebuild.log` +- CN1SS device log: + - `artifacts/device-runner.log` +- Decoded screenshots: + - `artifacts/*.png` +- Xcode derived data for this project: + - `~/Library/Developer/Xcode/DerivedData/HelloCodenameOne-*` + +## Notes + +- `scripts/hellocodenameone/ios/pom.xml` includes Objective-C files from `src/main/objectivec` as resources; headers should only include imports that are available in generated project context. +- If a native interface exists in common, platform impl classes should exist for every built platform variant used by generated stubs (at least android/ios/javase in this project). + +## Lessons Learned (Local Notifications + White Screenshots Incident) + +- Keep only stable fixes in core code: + - Temporary diagnostics (`NSLog`, extra CN1SS/CN1SHOT channels, timeout tweaks) are useful for triage but should be removed once root cause is proven. +- Propagation is a common trap: + - Rebuilding `maven/ios` is not enough if generated `ios-source` is stale. + - Always delete `scripts/hellocodenameone/ios/target/...-ios-source` before regeneration when validating port/native changes. +- Simulator behavior differs from device behavior: + - Startup notification authorization can block CN1SS runs with an iOS modal. + - A correct simulator-safe fix is to avoid notification authorization prompts on simulator startup paths. +- Stale SpringBoard state can mask fixes: + - After changing notification authorization behavior, erase the simulator before re-testing: + - `xcrun simctl shutdown ` + - `xcrun simctl erase ` +- Validate assumptions with direct evidence: + - Capture live simulator screenshots while tests run. + - Query logs directly with `simctl log show` for app process markers to confirm which code path actually executed. diff --git a/skills/ios-cn1ss-xcode-debug/agents/openai.yaml b/skills/ios-cn1ss-xcode-debug/agents/openai.yaml new file mode 100644 index 0000000000..b2e7c7c4eb --- /dev/null +++ b/skills/ios-cn1ss-xcode-debug/agents/openai.yaml @@ -0,0 +1,4 @@ +version: 1 +display_name: iOS CN1SS Xcode Debug +short_description: Reproduce and triage CN1SS iOS build/test failures with local Xcode logs. +default_prompt: Build local CN1 iOS snapshots, regenerate HelloCodenameOne ios-source offline, run xcodebuild and the CN1SS iOS runner, then summarize the first concrete failure and likely cause. From ec99645cb7bab5b1a31335393dda1546d17b1c10 Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 22 Feb 2026 19:42:16 +0200 Subject: [PATCH 7/8] Fixed bad native interface implementations --- .../InPlaceEditViewNativeImpl.java | 14 ++++++++------ .../LocalNotificationNativeImpl.java | 15 +++++++++++++++ .../InPlaceEditViewNative.java | 2 +- .../tests/InPlaceEditViewTest.java | 19 ++++++++++++------- ...llocodenameone_InPlaceEditViewNativeImpl.h | 3 +-- ...llocodenameone_InPlaceEditViewNativeImpl.m | 2 +- ...ocodenameone_LocalNotificationNativeImpl.h | 8 +++----- ...ocodenameone_LocalNotificationNativeImpl.m | 14 +++++++------- ..._hellocodenameone_InPlaceEditViewNative.js | 15 +++++++++++++++ ...ellocodenameone_LocalNotificationNative.js | 19 +++++++++++++++++++ .../InPlaceEditViewNativeImpl.java | 11 +++++++++++ .../LocalNotificationNativeImpl.java | 15 +++++++++++++++ .../InPlaceEditViewNativeImpl.cs | 13 +++++++++++++ .../LocalNotificationNativeImpl.cs | 17 +++++++++++++++++ 14 files changed, 138 insertions(+), 29 deletions(-) create mode 100644 scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java create mode 100644 scripts/hellocodenameone/javascript/src/main/javascript/com_codenameone_examples_hellocodenameone_InPlaceEditViewNative.js create mode 100644 scripts/hellocodenameone/javascript/src/main/javascript/com_codenameone_examples_hellocodenameone_LocalNotificationNative.js create mode 100644 scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java create mode 100644 scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java create mode 100644 scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.cs create mode 100644 scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.cs diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java index 0091c4fcac..0a9bf2623f 100644 --- a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java +++ b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java @@ -7,9 +7,10 @@ import com.codename1.ui.layouts.BoxLayout; import com.codename1.impl.android.InPlaceEditView; import com.codename1.impl.android.AndroidImplementation; +import com.codenameone.examples.hellocodenameone.tests.InPlaceEditViewTest; public class InPlaceEditViewNativeImpl { - public void runReproductionTest(final ReproductionCallback callback) { + public void runReproductionTest() { Display.getInstance().callSerially(() -> { try { java.lang.reflect.Method getImplMethod = Display.class.getDeclaredMethod("getImplementation"); @@ -17,8 +18,8 @@ public void runReproductionTest(final ReproductionCallback callback) { final Object impl = getImplMethod.invoke(Display.getInstance()); if (!(impl instanceof AndroidImplementation)) { - Display.getInstance().callSerially(() -> callback.onResult(false, "Implementation is not AndroidImplementation: " + impl.getClass().getName())); - return; + Display.getInstance().callSerially(() -> InPlaceEditViewTest.onError("Implementation is not AndroidImplementation: " + impl.getClass().getName())); + return; } final AndroidImplementation androidImpl = (AndroidImplementation) impl; @@ -57,16 +58,16 @@ public void runReproductionTest(final ReproductionCallback callback) { } }); } - Display.getInstance().callSerially(() -> callback.onResult(true, null)); + Display.getInstance().callSerially(() -> InPlaceEditViewTest.onSuccess()); } catch (Throwable t) { t.printStackTrace(); - Display.getInstance().callSerially(() -> callback.onResult(false, t.toString())); + Display.getInstance().callSerially(() -> InPlaceEditViewTest.onError(false, t.toString())); } }).start(); } catch (Throwable t) { t.printStackTrace(); - Display.getInstance().callSerially(() -> callback.onResult(false, t.toString())); + Display.getInstance().callSerially(() -> InPlaceEditViewTest.onError(t.toString())); } }); } @@ -74,4 +75,5 @@ public void runReproductionTest(final ReproductionCallback callback) { public boolean isSupported() { return true; } + } diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java new file mode 100644 index 0000000000..c9f92c64b9 --- /dev/null +++ b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java @@ -0,0 +1,15 @@ +package com.codenameone.examples.hellocodenameone; + +public class LocalNotificationNativeImpl { + public void clearScheduledLocalNotifications(String param) { + } + + public int getScheduledLocalNotificationCount(String param) { + return 0; + } + + public boolean isSupported() { + return false; + } + +} diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNative.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNative.java index c936ed9873..36f274191a 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNative.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNative.java @@ -3,5 +3,5 @@ import com.codename1.system.NativeInterface; public interface InPlaceEditViewNative extends NativeInterface { - void runReproductionTest(ReproductionCallback callback); + void runReproductionTest(); } diff --git a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InPlaceEditViewTest.java b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InPlaceEditViewTest.java index 599be9b275..041d4849a0 100644 --- a/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InPlaceEditViewTest.java +++ b/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/InPlaceEditViewTest.java @@ -5,6 +5,8 @@ import com.codename1.ui.Display; public class InPlaceEditViewTest extends BaseTest { + private static InPlaceEditViewTest instance; + @Override public boolean shouldTakeScreenshot() { return false; @@ -12,18 +14,21 @@ public boolean shouldTakeScreenshot() { @Override public boolean runTest() throws Exception { + instance = this; InPlaceEditViewNative nativeInterface = NativeLookup.create(InPlaceEditViewNative.class); if (nativeInterface != null && nativeInterface.isSupported()) { - nativeInterface.runReproductionTest((success, error) -> { - if (!success) { - fail("Reproduction test failed: " + error); - } else { - done(); - } - }); + nativeInterface.runReproductionTest(); } else { done(); } return true; } + + public static void onSuccess() { + instance.done(); + } + + public static void onError(String error) { + instance.fail("Reproduction test failed: " + error); + } } diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl.h b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl.h index fa7ba872c8..6c20a29175 100644 --- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl.h +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl.h @@ -3,7 +3,6 @@ @interface com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl : NSObject { } --(void)runReproductionTest:(id)param; +-(void)runReproductionTest; -(BOOL)isSupported; - @end diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl.m b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl.m index 65387fd891..d83d25e941 100644 --- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl.m +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl.m @@ -2,7 +2,7 @@ @implementation com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl --(void)runReproductionTest:(id)param{ +-(void)runReproductionTest{ } -(BOOL)isSupported{ diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h index 10721d827c..bb2edfa8a4 100644 --- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h @@ -1,11 +1,9 @@ #import -#import "CN1ObjcProxy.h" @interface com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl : NSObject { } -- (void)clearScheduledLocalNotifications:(NSString*)notificationId; -- (int)getScheduledLocalNotificationCount:(NSString*)notificationId; -- (BOOL)isSupported; - +-(void)clearScheduledLocalNotifications:(NSString*)param; +-(int)getScheduledLocalNotificationCount:(NSString*)param; +-(BOOL)isSupported; @end diff --git a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m index 08c38cb767..a0b075c8f7 100644 --- a/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m +++ b/scripts/hellocodenameone/ios/src/main/objectivec/com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.m @@ -3,8 +3,8 @@ @implementation com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl -- (void)clearScheduledLocalNotifications:(NSString*)notificationId { - if (notificationId == nil) { +-(void)clearScheduledLocalNotifications:(NSString*)param{ + if (param == nil) { return; } dispatch_sync(dispatch_get_main_queue(), ^{ @@ -13,15 +13,15 @@ - (void)clearScheduledLocalNotifications:(NSString*)notificationId { for (UILocalNotification *notification in scheduled) { NSDictionary *userInfo = notification.userInfo; NSString *uid = [NSString stringWithFormat:@"%@", [userInfo valueForKey:@"__ios_id__"]]; - if ([notificationId isEqualToString:uid]) { + if ([param isEqualToString:uid]) { [app cancelLocalNotification:notification]; } } }); } -- (int)getScheduledLocalNotificationCount:(NSString*)notificationId { - if (notificationId == nil) { +-(int)getScheduledLocalNotificationCount:(NSString*)param{ + if (param == nil) { return 0; } __block int count = 0; @@ -31,7 +31,7 @@ - (int)getScheduledLocalNotificationCount:(NSString*)notificationId { for (UILocalNotification *notification in scheduled) { NSDictionary *userInfo = notification.userInfo; NSString *uid = [NSString stringWithFormat:@"%@", [userInfo valueForKey:@"__ios_id__"]]; - if ([notificationId isEqualToString:uid]) { + if ([param isEqualToString:uid]) { count++; } } @@ -39,7 +39,7 @@ - (int)getScheduledLocalNotificationCount:(NSString*)notificationId { return count; } -- (BOOL)isSupported { +-(BOOL)isSupported{ return YES; } diff --git a/scripts/hellocodenameone/javascript/src/main/javascript/com_codenameone_examples_hellocodenameone_InPlaceEditViewNative.js b/scripts/hellocodenameone/javascript/src/main/javascript/com_codenameone_examples_hellocodenameone_InPlaceEditViewNative.js new file mode 100644 index 0000000000..e95a812409 --- /dev/null +++ b/scripts/hellocodenameone/javascript/src/main/javascript/com_codenameone_examples_hellocodenameone_InPlaceEditViewNative.js @@ -0,0 +1,15 @@ +(function(exports){ + +var o = {}; + + o.runReproductionTest_ = function(callback) { + callback.error(new Error("Not implemented yet")); + }; + + o.isSupported_ = function(callback) { + callback.complete(false); + }; + +exports.com_codenameone_examples_hellocodenameone_InPlaceEditViewNative= o; + +})(cn1_get_native_interfaces()); diff --git a/scripts/hellocodenameone/javascript/src/main/javascript/com_codenameone_examples_hellocodenameone_LocalNotificationNative.js b/scripts/hellocodenameone/javascript/src/main/javascript/com_codenameone_examples_hellocodenameone_LocalNotificationNative.js new file mode 100644 index 0000000000..7528bfc5da --- /dev/null +++ b/scripts/hellocodenameone/javascript/src/main/javascript/com_codenameone_examples_hellocodenameone_LocalNotificationNative.js @@ -0,0 +1,19 @@ +(function(exports){ + +var o = {}; + + o.clearScheduledLocalNotifications__java_lang_String = function(param1, callback) { + callback.error(new Error("Not implemented yet")); + }; + + o.getScheduledLocalNotificationCount__java_lang_String = function(param1, callback) { + callback.error(new Error("Not implemented yet")); + }; + + o.isSupported_ = function(callback) { + callback.complete(false); + }; + +exports.com_codenameone_examples_hellocodenameone_LocalNotificationNative= o; + +})(cn1_get_native_interfaces()); diff --git a/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java b/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java new file mode 100644 index 0000000000..edd7a45311 --- /dev/null +++ b/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java @@ -0,0 +1,11 @@ +package com.codenameone.examples.hellocodenameone; + +public class InPlaceEditViewNativeImpl implements com.codenameone.examples.hellocodenameone.InPlaceEditViewNative{ + public void runReproductionTest() { + } + + public boolean isSupported() { + return false; + } + +} diff --git a/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java b/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java new file mode 100644 index 0000000000..f12c1dd122 --- /dev/null +++ b/scripts/hellocodenameone/javase/src/main/java/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.java @@ -0,0 +1,15 @@ +package com.codenameone.examples.hellocodenameone; + +public class LocalNotificationNativeImpl implements com.codenameone.examples.hellocodenameone.LocalNotificationNative{ + public void clearScheduledLocalNotifications(String param) { + } + + public int getScheduledLocalNotificationCount(String param) { + return 0; + } + + public boolean isSupported() { + return false; + } + +} diff --git a/scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.cs b/scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.cs new file mode 100644 index 0000000000..05661041f4 --- /dev/null +++ b/scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.cs @@ -0,0 +1,13 @@ +namespace com.codenameone.examples.hellocodenameone{ + + +public class InPlaceEditViewNativeImpl : IInPlaceEditViewNativeImpl { + public void runReproductionTest() { + } + + public bool isSupported() { + return false; + } + +} +} diff --git a/scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.cs b/scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.cs new file mode 100644 index 0000000000..940dd06c6a --- /dev/null +++ b/scripts/hellocodenameone/win/src/main/csharp/com/codenameone/examples/hellocodenameone/LocalNotificationNativeImpl.cs @@ -0,0 +1,17 @@ +namespace com.codenameone.examples.hellocodenameone{ + + +public class LocalNotificationNativeImpl : ILocalNotificationNativeImpl { + public void clearScheduledLocalNotifications(String param) { + } + + public int getScheduledLocalNotificationCount(String param) { + return 0; + } + + public bool isSupported() { + return false; + } + +} +} From eba011267edc7e3c490af59ce3b69d07ad56c12a Mon Sep 17 00:00:00 2001 From: Shai Almog <67850168+shai-almog@users.noreply.github.com> Date: Sun, 22 Feb 2026 20:35:09 +0200 Subject: [PATCH 8/8] Another attempt at fixing the permission prompt problem --- .../nativeSources/CodenameOne_GLAppDelegate.m | 2 ++ Ports/iOSPort/nativeSources/IOSNative.m | 19 ------------------- .../InPlaceEditViewNativeImpl.java | 2 +- 3 files changed, 3 insertions(+), 20 deletions(-) diff --git a/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.m b/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.m index 0c15173d1f..02e4b3c3c7 100644 --- a/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.m +++ b/Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.m @@ -181,9 +181,11 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( if (isIOS10()) { UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; center.delegate = self; +#if !TARGET_OS_SIMULATOR [center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error){ if( !error ) {} }]; +#endif } } #endif diff --git a/Ports/iOSPort/nativeSources/IOSNative.m b/Ports/iOSPort/nativeSources/IOSNative.m index bf5ac2d4fd..adc39439f1 100644 --- a/Ports/iOSPort/nativeSources/IOSNative.m +++ b/Ports/iOSPort/nativeSources/IOSNative.m @@ -5850,9 +5850,6 @@ void com_codename1_impl_ios_IOSNative_sendSMS___java_lang_String_java_lang_Strin void com_codename1_impl_ios_IOSNative_registerPush__(CN1_THREAD_STATE_MULTI_ARG JAVA_OBJECT instanceObject) { #ifdef INCLUDE_CN1_PUSH2 dispatch_async(dispatch_get_main_queue(), ^{ -#if TARGET_OS_SIMULATOR - return; -#endif if (@available(iOS 10, *)) { // iOS 10 ObjC code pendingRemoteNotificationRegistrations++; @@ -9830,28 +9827,14 @@ JAVA_VOID com_codename1_impl_ios_IOSNative_sendLocalNotification___java_lang_Str authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; } -#if TARGET_OS_SIMULATOR - NSArray *ids = @[toNSString(CN1_THREAD_STATE_PASS_ARG notificationId)]; - [center removePendingNotificationRequestsWithIdentifiers:ids]; - [center removeDeliveredNotificationsWithIdentifiers:ids]; - [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { - if (error != nil) { - NSLog(@"%@", error.localizedDescription); - } - }]; -#else [center requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) { - NSArray *ids = @[toNSString(CN1_THREAD_STATE_PASS_ARG notificationId)]; - [center removePendingNotificationRequestsWithIdentifiers:ids]; - [center removeDeliveredNotificationsWithIdentifiers:ids]; [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) { if (error != nil) { NSLog(@"%@", error.localizedDescription); } }]; }]; -#endif }); @@ -9897,11 +9880,9 @@ JAVA_VOID com_codename1_impl_ios_IOSNative_sendLocalNotification___java_lang_Str dispatch_sync(dispatch_get_main_queue(), ^{ cn1CancelScheduledLocalNotificationById(notificationIdString); #ifdef __IPHONE_8_0 -#if !TARGET_OS_SIMULATOR if ([UIApplication instancesRespondToSelector:@selector(registerUserNotificationSettings:)]){ [[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound categories:nil]]; } -#endif #endif [[UIApplication sharedApplication] scheduleLocalNotification: notification]; }); diff --git a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java index 0a9bf2623f..7a3733b6d5 100644 --- a/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java +++ b/scripts/hellocodenameone/android/src/main/java/com/codenameone/examples/hellocodenameone/InPlaceEditViewNativeImpl.java @@ -61,7 +61,7 @@ public void runReproductionTest() { Display.getInstance().callSerially(() -> InPlaceEditViewTest.onSuccess()); } catch (Throwable t) { t.printStackTrace(); - Display.getInstance().callSerially(() -> InPlaceEditViewTest.onError(false, t.toString())); + Display.getInstance().callSerially(() -> InPlaceEditViewTest.onError(t.toString())); } }).start();