Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Ports/iOSPort/nativeSources/CodenameOne_GLAppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 20 additions & 11 deletions Ports/iOSPort/nativeSources/IOSNative.m
Original file line number Diff line number Diff line change
Expand Up @@ -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
) {
Expand Down Expand Up @@ -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"];
}
Expand Down Expand Up @@ -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]];
Expand All @@ -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);
});
}

Expand Down
2 changes: 2 additions & 0 deletions docs/developer-guide/Working-With-iOS.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@
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");
getImplMethod.setAccessible(true);
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;

Expand Down Expand Up @@ -57,21 +58,22 @@ 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(t.toString()));
}
}).start();

} catch (Throwable t) {
t.printStackTrace();
Display.getInstance().callSerially(() -> callback.onResult(false, t.toString()));
Display.getInstance().callSerially(() -> InPlaceEditViewTest.onError(t.toString()));
}
});
}

public boolean isSupported() {
return true;
}

}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
import com.codename1.system.NativeInterface;

public interface InPlaceEditViewNative extends NativeInterface {
void runReproductionTest(ReproductionCallback callback);
void runReproductionTest();
}
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,30 @@
import com.codename1.ui.Display;

public class InPlaceEditViewTest extends BaseTest {
private static InPlaceEditViewTest instance;

@Override
public boolean shouldTakeScreenshot() {
return false;
}

@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);
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
@interface com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl : NSObject {
}

-(void)runReproductionTest:(id)param;
-(void)runReproductionTest;
-(BOOL)isSupported;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

@implementation com_codenameone_examples_hellocodenameone_InPlaceEditViewNativeImpl

-(void)runReproductionTest:(id)param{
-(void)runReproductionTest{
}

-(BOOL)isSupported{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#import <Foundation/Foundation.h>

@interface com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl : NSObject {
}

-(void)clearScheduledLocalNotifications:(NSString*)param;
-(int)getScheduledLocalNotificationCount:(NSString*)param;
-(BOOL)isSupported;
@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#import "com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl.h"
#import <UIKit/UIKit.h>

@implementation com_codenameone_examples_hellocodenameone_LocalNotificationNativeImpl

-(void)clearScheduledLocalNotifications:(NSString*)param{
if (param == 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 ([param isEqualToString:uid]) {
[app cancelLocalNotification:notification];
}
}
});
}

-(int)getScheduledLocalNotificationCount:(NSString*)param{
if (param == 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 ([param isEqualToString:uid]) {
count++;
}
}
});
return count;
}

-(BOOL)isSupported{
return YES;
}

@end
Original file line number Diff line number Diff line change
@@ -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());
Original file line number Diff line number Diff line change
@@ -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());
Original file line number Diff line number Diff line change
@@ -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;
}

}
Loading
Loading