Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Enable APNS registration + notification delivery in macOS apps #33574

Merged
merged 34 commits into from Jul 12, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d7b5d07
feat: enable APNS registration
joanx Mar 30, 2022
bf05601
fix: add APNS register/unregister methods to app
joanx Mar 30, 2022
48f2573
minor wording edit (kick off CircleCI)
joanx May 2, 2022
21b0770
fix: lint
joanx May 7, 2022
d619a96
fix: build errors
joanx May 9, 2022
4debddf
fix: undo semantic override
joanx May 9, 2022
a2f0258
fix: clean up push_notifications header file
joanx May 9, 2022
b692619
fix: enable pushNotifications module visibility
joanx May 18, 2022
7e5f839
fix: WIP resolve build errors
joanx May 18, 2022
e842356
fix: add types, misc. cleanup
joanx May 18, 2022
9af65b5
fix: type reference
joanx May 19, 2022
1d60214
fix: cleanup
joanx May 19, 2022
a1d6309
fix: remove delegate-setting logic
joanx May 25, 2022
20fd646
fix: lint
joanx May 26, 2022
2fdb31d
fix: build failure
joanx May 26, 2022
66cde9e
fix: attempt fix build 2
joanx May 26, 2022
5dac57e
force builds
joanx Jun 7, 2022
948fd59
force builds
joanx Jun 8, 2022
563e323
Merge branch 'main' into enable-apns
joanx Jun 8, 2022
662bf3b
fix: rm LICENSES.chromium.html
joanx Jun 8, 2022
30186f7
fix: gen cleanup
joanx Jun 8, 2022
c3ea89e
Merge branch 'main' into enable-apns
joanx Jun 8, 2022
5e78aae
fix: decouple implementation from Browser object
joanx Jun 16, 2022
7443a87
fix: cleanup
joanx Jun 16, 2022
d590b5a
fix: add general push_notifications module
joanx Jun 16, 2022
220e590
fix: cleanup
joanx Jun 16, 2022
312ba2d
fix: rm unneeded imports
joanx Jun 17, 2022
055fafc
fix: variable renaming + update get handler
joanx Jun 24, 2022
11055ce
fix: cleanup
joanx Jun 24, 2022
8bc184a
fix: lint + update docs
joanx Jun 29, 2022
0508d7b
fix: add example to docs
joanx Jun 29, 2022
90443e5
fix: naming + avoid list mutation
joanx Jun 29, 2022
1d00714
fix: apns_promise_set as class variable
joanx Jun 29, 2022
9802079
fix: lint
joanx Jun 29, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions docs/api/app.md
Expand Up @@ -1442,6 +1442,16 @@ details.

**Note:** Enable `Secure Keyboard Entry` only when it is needed and disable it when it is no longer needed.

### `app.registerForRemoteNotifications()` _macOS_

Registers this app with Apple Push Notification service (APNS) to receive [Badge, Sound, and Alert](https://developer.apple.com/documentation/appkit/sremotenotificationtype?language=objc) notifications. If registration is successful, the BrowserWindow event `registered-for-remote-notifications` will be emitted with the APNS device token. Otherwise, the event `failed-to-register-for-remote-notifications` will be emitted.
See: https://developer.apple.com/documentation/appkit/nsapplication/1428476-registerforremotenotificationtyp?language=objc

### `app.unregisterForRemoteNotifications()` _macOS_

Unregisters the app for notifications received from APNS.
See: https://developer.apple.com/documentation/appkit/nsapplication/1428747-unregisterforremotenotifications?language=objc

## Properties

### `app.accessibilitySupportEnabled` _macOS_ _Windows_
Expand Down
27 changes: 27 additions & 0 deletions docs/api/browser-window.md
Expand Up @@ -687,6 +687,33 @@ Emitted when the window has closed a sheet.

Emitted when the native new tab button is clicked.

#### Event: 'registered-for-remote-notifications' _macOS_

Returns:

* `token` String - A token that identifies the device to Apple Push Notification Service (APNS)

Emitted when the app successfully registers to receive remote notifications from APNS.
See: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428766-application?language=objc

#### Event: 'failed-to-register-for-remote-notifications' _macOS_

Returns:

* `error` String

Emitted when the app fails to register to receive remote notifications from APNS.
See: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428554-application?language=objc

#### Event: 'received-remote-notification' _macOS_

Returns:

* `userInfo` Record<String, any>

Emitted when the app receives a remote notification while running.
See: https://developer.apple.com/documentation/appkit/nsapplicationdelegate/1428430-application?language=objc

#### Event: 'system-context-menu' _Windows_

Returns:
Expand Down
21 changes: 21 additions & 0 deletions shell/browser/api/electron_api_app.cc
Expand Up @@ -783,6 +783,21 @@ void App::OnNewWindowForTab() {
void App::OnDidBecomeActive() {
Emit("did-become-active");
}

joanx marked this conversation as resolved.
Show resolved Hide resolved
void App::OnDidRegisterForRemoteNotificationsWithDeviceToken(
const std::string& token) {
Emit("registered-for-remote-notifications", token);
}

void App::OnDidFailToRegisterForRemoteNotificationsWithError(
const std::string& error) {
Emit("failed-to-register-for-remote-notifications", error);
}

void App::OnDidReceiveRemoteNotification(
const base::DictionaryValue& user_info) {
Emit("received-remote-notification", user_info);
}
#endif

bool App::CanCreateWindow(
Expand Down Expand Up @@ -1784,6 +1799,12 @@ gin::ObjectTemplateBuilder App::GetObjectTemplateBuilder(v8::Isolate* isolate) {
.SetMethod("moveToApplicationsFolder", &App::MoveToApplicationsFolder)
.SetMethod("isInApplicationsFolder", &App::IsInApplicationsFolder)
.SetMethod("setActivationPolicy", &App::SetActivationPolicy)
.SetMethod("registerForRemoteNotifications",
base::BindRepeating(&Browser::RegisterForRemoteNotifications,
browser))
.SetMethod("unregisterForRemoteNotifications",
base::BindRepeating(&Browser::UnregisterForRemoteNotifications,
browser))
#endif
.SetMethod("setAboutPanelOptions",
base::BindRepeating(&Browser::SetAboutPanelOptions, browser))
Expand Down
6 changes: 6 additions & 0 deletions shell/browser/api/electron_api_app.h
Expand Up @@ -118,6 +118,12 @@ class App : public ElectronBrowserClient::Delegate,
const base::DictionaryValue& user_info) override;
void OnNewWindowForTab() override;
void OnDidBecomeActive() override;
void OnDidRegisterForRemoteNotificationsWithDeviceToken(
const std::string& token) override;
void OnDidFailToRegisterForRemoteNotificationsWithError(
const std::string& error) override;
void OnDidReceiveRemoteNotification(
const base::DictionaryValue& user_info) override;
#endif

// content::ContentBrowserClient:
Expand Down
17 changes: 17 additions & 0 deletions shell/browser/browser.h
Expand Up @@ -227,6 +227,23 @@ class Browser : public WindowListObserver {
// Set docks' icon.
void DockSetIcon(v8::Isolate* isolate, v8::Local<v8::Value> icon);

// Register with APNS
void RegisterForRemoteNotifications();
void UnregisterForRemoteNotifications();

// Indicates that APNS registration succeeded, the token can be used to send
// notifications.
void DidRegisterForRemoteNotificationsWithDeviceToken(
const std::string& token);

// Indicates a failure to register for APNS
void DidFailToRegisterForRemoteNotificationsWithError(
const std::string& error);

// Indicates that a new remote notification has been received while the app is
// running.
void DidReceiveRemoteNotification(const base::DictionaryValue& user_info);

#endif // BUILDFLAG(IS_MAC)

void ShowAboutPanel();
Expand Down
30 changes: 30 additions & 0 deletions shell/browser/browser_mac.mm
Expand Up @@ -532,4 +532,34 @@ void RemoveFromLoginItems() {
}
}

void Browser::RegisterForRemoteNotifications() {
[[AtomApplication sharedApplication]
registerForRemoteNotificationTypes:NSRemoteNotificationTypeBadge |
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Attempting to use the updated API (registerForRemoteNotifications) resulted in this error:

'registerForRemoteNotifications' has been marked as being introduced in macOS 10.14 here, but the deployment target is macOS 10.11.0

NSRemoteNotificationTypeAlert |
NSRemoteNotificationTypeSound];

}

void Browser::UnregisterForRemoteNotifications() {
[[AtomApplication sharedApplication] unregisterForRemoteNotifications];
}

void Browser::DidRegisterForRemoteNotificationsWithDeviceToken(
const std::string& token) {
for (BrowserObserver& observer : observers_)
observer.OnDidRegisterForRemoteNotificationsWithDeviceToken(token);
}

void Browser::DidFailToRegisterForRemoteNotificationsWithError(
const std::string& error) {
for (BrowserObserver& observer : observers_)
observer.OnDidFailToRegisterForRemoteNotificationsWithError(error);
}

void Browser::DidReceiveRemoteNotification(
const base::DictionaryValue& user_info) {
for (BrowserObserver& observer : observers_)
observer.OnDidReceiveRemoteNotification(user_info);
}

} // namespace electron
12 changes: 12 additions & 0 deletions shell/browser/browser_observer.h
Expand Up @@ -86,6 +86,18 @@ class BrowserObserver : public base::CheckedObserver {

// Browser did become active.
virtual void OnDidBecomeActive() {}

// The browser successfully registered for APNS. (macOS only)
virtual void OnDidRegisterForRemoteNotificationsWithDeviceToken(
const std::string& token) {}

// The browser failed to register for APNS. (macOS only)
virtual void OnDidFailToRegisterForRemoteNotificationsWithError(
const std::string& error) {}

// The browser received a remote notification from APNS (macOS only)
virtual void OnDidReceiveRemoteNotification(
const base::DictionaryValue& user_info) {}
#endif

protected:
Expand Down
27 changes: 27 additions & 0 deletions shell/browser/mac/electron_application_delegate.mm
Expand Up @@ -191,4 +191,31 @@ - (IBAction)newWindowForTab:(id)sender {
electron::Browser::Get()->NewWindowForTab();
}

- (void)application:(NSApplication*)application
didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
// https://stackoverflow.com/a/16411517
const char* tokenData = static_cast<const char*>([deviceToken bytes]);
joanx marked this conversation as resolved.
Show resolved Hide resolved
NSMutableString* tokenString = [NSMutableString string];
for (NSUInteger i = 0; i < [deviceToken length]; i++) {
[tokenString appendFormat:@"%02.2hhX", tokenData[i]];
}
electron::Browser::Get()->DidRegisterForRemoteNotificationsWithDeviceToken(
base::SysNSStringToUTF8(tokenString));
}

- (void)application:(NSApplication*)application
didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {
std::string error_message(base::SysNSStringToUTF8(
[NSString stringWithFormat:@"%ld %@ %@", [error code], [error domain],
[error userInfo]]));
electron::Browser::Get()->DidFailToRegisterForRemoteNotificationsWithError(
error_message);
}

- (void)application:(NSApplication*)application
didReceiveRemoteNotification:(NSDictionary*)userInfo {
electron::Browser::Get()->DidReceiveRemoteNotification(
electron::NSDictionaryToDictionaryValue(userInfo));
}

@end