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

@mczernek/notifications suspended after 5 minutes #9287

Merged
merged 17 commits into from Jul 30, 2020

Conversation

mczernek
Copy link
Contributor

@mczernek mczernek commented Jul 17, 2020

Why

Current architecture of expo-notifications is based in many places on invoking JobIntentService for each small action. While it provided nice and concise way of handling asynchronous tasks, it cause some of those actions to be deffered until Android decides to perform them. That meant, that after five minutes of inactivity on the phone (blocked and disconnected from charger), those actions were queued for later execution.

One of such actions was handling of just received notification. As a result, after receiving call from remote (FCM) notification was not displayed right away, even though app was being waked by system.

It is described in #9037

How

issue is fixed by converting most of those actions from execution in JobIntentService into straight synchronous code.

Mentioned service served not only as as asynchronity provider, but as a way of scoping for Notifications. Since some code in the module is executed without ModuleRegistry, we could not use standard scoping mechanism. As a result, I opted to creating bare and expoview flavors. Thanks to small modification in gradle.groovy this should come unnoticed to users and expo developers. At the same time it provides great way of scoping when we can't use ModuleRegistry.

Test Plan

test-suite and ncl - both bare and expo client.

@github-actions
Copy link
Contributor

github-actions bot commented Jul 17, 2020

Fails
🚫

📋 Missing Changelog

🛠 Add missing entries to:

🛠 Suggested fixes:

📋 Missing changelog

Apply suggested changes:

diff --git a/packages/react-native-unimodules/CHANGELOG.md b/packages/react-native-unimodules/CHANGELOG.md
index 56ffe442..591b8a71 100644
--- a/packages/react-native-unimodules/CHANGELOG.md
+++ b/packages/react-native-unimodules/CHANGELOG.md
@@ -10,6 +10,8 @@
 
 ### 🐛 Bug fixes
 
+- @mczernek/notifications suspended after 5 minutes. ([#9287](https://github.com/expo/expo/pull/9287) by [@mczernek](https://github.com/mczernek))
+
 ## 0.10.1 — 2020-05-29
 
 ### 📚 Library updates

diff --git a/packages/unimodules-app-loader/CHANGELOG.md b/packages/unimodules-app-loader/CHANGELOG.md
index 12960222..67b9c3a4 100644
--- a/packages/unimodules-app-loader/CHANGELOG.md
+++ b/packages/unimodules-app-loader/CHANGELOG.md
@@ -8,6 +8,8 @@
 
 ### 🐛 Bug fixes
 
+- @mczernek/notifications suspended after 5 minutes. ([#9287](https://github.com/expo/expo/pull/9287) by [@mczernek](https://github.com/mczernek))
+
 ## 1.2.0 — 2020-05-29
 
 *This version does not introduce any user-facing changes.*

Generated by 🚫 dangerJS against 0f274e7

@github-actions
Copy link
Contributor

Native Component List for this branch is ready

Copy link
Contributor

@esamelson esamelson left a comment

Choose a reason for hiding this comment

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

Thanks for jumping on this! My main comment is that it seems a little bit overkill to add the whole expoview flavor and infrastructure to this module just for the one Reconstructor class. Is it possible to just keep all the expoview code in the root android/src/expoview directory and override this class from there? I think that as much as possible, the packages should not need to know about the Expo client and when we do need to have special case code for the client, that code should live in the client's codebase rather than in the individual packages. The packages themselves should not need to worry about supporting the Expo client special case and I think it's more confusing to have that code be distributed throughout all the packages rather than collated in one location.

Otherwise it lgtm, but would also want to make sure @lukmccall OKs the architecture changes in this PR before landing, since @sjchmiela is currently OOO.

@mczernek
Copy link
Contributor Author

Thanks for jumping on this! My main comment is that it seems a little bit overkill to add the whole expoview flavor and infrastructure to this module just for the one Reconstructor class. Is it possible to just keep all the expoview code in the root android/src/expoview directory and override this class from there? I think that as much as possible, the packages should not need to know about the Expo client and when we do need to have special case code for the client, that code should live in the client's codebase rather than in the individual packages. The packages themselves should not need to worry about supporting the Expo client special case and I think it's more confusing to have that code be distributed throughout all the packages rather than collated in one location.

Otherwise it lgtm, but would also want to make sure @lukmccall OKs the architecture changes in this PR before landing, since @sjchmiela is currently OOO.

I do not know of any reliable way of substituting class implementation other than android flavors. We do have mechanism for that based on ModuleRegistry, but it is not applicable, since we do not have access to it.

Another solution I implemented in expo-task-manager is by declaring actual implementation in AndroidManifest's meta-data. I find flavor solutions much friendlier and was considering switching to it in task manager as well, given we got agreement on that.

Another solution I could potentially think of is avoiding scoping reconstructor whatsoever. I guess it would be doable. But instead of walking around the problem I've wanted to try and discuss a solution that seemed clean to me.

@esamelson
Copy link
Contributor

Gotcha, thanks for the explanation. I would strongly lean towards the AndroidManifest solution that you used for expo-task-manager, for these reasons:

  • it puts the responsibility on expoview for enabling the extra scoping in the managed workflow (which should be treated as the special case here), rather than putting responsibility on react-native-unimodules to hide the extra flavor in bare workflow projects (which are the general case)
  • maintainability for developers touching the expoview code in the future. Confusion about how the class is being overridden in AndroidManifest can be headed off by explaining it in comments, but my opinion is that expoview as a library becomes harder to maintain if it's distributed around multiple directories/packages in this repo.

Happy to discuss/consider this further though!

Copy link
Contributor

@lukmccall lukmccall left a comment

Choose a reason for hiding this comment

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

Look good :D

I've left some comments ;)

@@ -78,6 +78,7 @@ public ExpoModuleRegistryAdapter(ReactModuleRegistryProvider moduleRegistryProvi
moduleRegistry.registerExportedModule(new ScopedExpoNotificationPresentationModule(scopedContext, experienceId));
moduleRegistry.registerInternalModule(new ScopedNotificationsChannelsProvider(scopedContext, experienceId));


Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change

🧹

@@ -59,6 +59,7 @@
new expo.modules.speech.SpeechPackage(),
new expo.modules.sqlite.SQLitePackage(),
new expo.modules.taskManager.TaskManagerPackage(),
new expo.modules.updates.UpdatesPackage(),
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if this line should be in this PR ;)

@@ -10,6 +10,7 @@

### 🐛 Bug fixes

- Fix notifications not being displayed after five minutes of phone inactivity on Android ([#9287](https://github.com/expo/expo/pull/9287) by [@mczernek](https://github.com/mczernek))
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
- Fix notifications not being displayed after five minutes of phone inactivity on Android ([#9287](https://github.com/expo/expo/pull/9287) by [@mczernek](https://github.com/mczernek))
- Fix notifications not being displayed after five minutes of phone inactivity on Android. ([#9287](https://github.com/expo/expo/pull/9287) by [@mczernek](https://github.com/mczernek))

🧹

@@ -344,7 +351,7 @@ protected void removeNotification(Context context, String identifier) {
/**
* Remove selected notifications from storage and cancel any pending alarms.
*
* @param context Context this is being called from
* @param context Context this is being called from
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
* @param context Context this is being called from
* @param context Context this is being called from

🧹

Copy link
Contributor

Choose a reason for hiding this comment

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

ping

@mczernek
Copy link
Contributor Author

Gotcha, thanks for the explanation. I would strongly lean towards the AndroidManifest solution that you used for expo-task-manager, for these reasons:

  • it puts the responsibility on expoview for enabling the extra scoping in the managed workflow (which should be treated as the special case here), rather than putting responsibility on react-native-unimodules to hide the extra flavor in bare workflow projects (which are the general case)
  • maintainability for developers touching the expoview code in the future. Confusion about how the class is being overridden in AndroidManifest can be headed off by explaining it in comments, but my opinion is that expoview as a library becomes harder to maintain if it's distributed around multiple directories/packages in this repo.

Happy to discuss/consider this further though!

I see your point and I could go with that. Will just have another try with convincing you to my solution :)

Flavours are very widely used concept in Android world, so it should not come with any surprise to those coming from Android. I believe we can get used to it as well :)
The only responsibility of react-native-unimodules is to set strategy for when flavour dimension is missing. In my humble opinion, that's much clearer than having class name typed in AndroidManifest's meta-data and recreating it with reflection.

Moreover, I am hoping that one day all scoping might be done with flavours. That would give us uniform and clear scoping with no need for reflection for that. Moreover, I can imagine in further future, that we just switch from client to standalone app by selecting the right flavour in expo repository.

Last but not least, I am all for having all module related code within the module. I believe it is much easier, while developing, say, expo-notifications to stick with expo-notifications module, rather than switching from respective module to expoview back and forth. I can see that is a matter of preference, though.

The way I see it, none of the solutions has clear advantage over the other. While I prefer mine, I can totally agree on rewriting it to yours given I failed to convince you :)

@esamelson
Copy link
Contributor

Your points make sense as well 😄 but I still want to push back a little:

Flavours are very widely used concept in Android world, so it should not come with any surprise to those coming from Android. I believe we can get used to it as well :)
The only responsibility of react-native-unimodules is to set strategy for when flavour dimension is missing. In my humble opinion, that's much clearer than having class name typed in AndroidManifest's meta-data and recreating it with reflection.

My hesitation is not to the flavors themselves but to the fact that we would be shipping internal-only flavors in an external-facing package. We've seen that when complications due to our client codebase sneak into externally-consumed packages, this tends to make things weirder and more difficult for developers (ExpoKit is an extreme example, but even the unimodule architecture itself is something we want to reduce), so I would strongly prefer to keep the packages as lean and close to other plain RN libraries as possible. However, if this type of practice (shipping flavors used for internal builds) is common among Android libraries I could be convinced otherwise! 😄

Last but not least, I am all for having all module related code within the module. I believe it is much easier, while developing, say, expo-notifications to stick with expo-notifications module, rather than switching from respective module to expoview back and forth. I can see that is a matter of preference, though.

This makes a lot of sense. One point in favor of keeping the scoped expoview code together, though, is that this is the convention we already follow for all the other scoped modules (which use ModuleRegistry), so this one would be a bit of an outlier.

@mczernek
Copy link
Contributor Author

As of your concern with shipping flavour specific code to users, we try to mitigate that by not publishing expoview specific code to npm in modules (as seen in all .npmignore files). There is still a risk that users might face problems here, especially when they try to set up unimodules on their own (without unimodules gradle plugin).

To sum up this discussion, we do not have one approach that would solve all the issues, but you still prefer yours to mine :) I am comfortable to migrate to yours then, just let me know my assumption is correct.

@esamelson
Copy link
Contributor

OK, yeah, that sounds good. Thanks for your willingness to discuss/change this!

@mczernek mczernek force-pushed the @mczernek/notifications-suspended-after-5-minutes branch from f1a49ab to 5094bd1 Compare July 27, 2020 12:32
Copy link
Contributor

@cruzach cruzach left a comment

Choose a reason for hiding this comment

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

I'm not as familiar as eric and lukasz with the rest of the expoview and expo-notifications infrastructure, respectively, so I defer to them for that, but this doesn't look like it breaks any of the recent notification category functionality, thanks 👍

Copy link
Contributor

@lukmccall lukmccall left a comment

Choose a reason for hiding this comment

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

LGTM 🚀🚀🚀

android/app/build.gradle Outdated Show resolved Hide resolved
Comment on lines -100 to +97
<provider
android:name="versioned.host.exp.exponent.modules.api.components.webview.RNCWebViewFileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if this change should be in this PR ;)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wow, that's weird. Must be some side effect of rebasing/upmerging. Will take a look at that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Really, there is only change in formatting. But GH shows the diff quite weirdly.

packages/expo-notifications/android/build.gradle Outdated Show resolved Hide resolved
@@ -344,7 +351,7 @@ protected void removeNotification(Context context, String identifier) {
/**
* Remove selected notifications from storage and cancel any pending alarms.
*
* @param context Context this is being called from
* @param context Context this is being called from
Copy link
Contributor

Choose a reason for hiding this comment

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

ping

packages/react-native-unimodules/gradle.groovy Outdated Show resolved Hide resolved
@mczernek mczernek merged commit d40fb97 into master Jul 30, 2020
@mczernek mczernek deleted the @mczernek/notifications-suspended-after-5-minutes branch July 30, 2020 08:55
sjchmiela added a commit that referenced this pull request Aug 21, 2020
…tes too (#9816)

# Why

Expansion of #9287. Before this pull request `ScheduledAlarmReceiver` enqueues work to `ExpoNotificationSchedulerService` which may be postponed by Android system indefinitely. Simply making the `ExpoNotificationSchedulerService` a POJO will make `ScheduledAlarmReceiver` not try to enqueue work to another service, but execute it immediately.

# How

Renamed `ExpoNotificationSchedulerService` to `NotificationSchedulingHelper` and made it a POJO with only the `enqueue*` methods being public and available.

# Test Plan

I've verified that tests related to scheduling pass in `test-suite`.
bbarthec pushed a commit that referenced this pull request Aug 24, 2020
…tes too (#9816)

# Why

Expansion of #9287. Before this pull request `ScheduledAlarmReceiver` enqueues work to `ExpoNotificationSchedulerService` which may be postponed by Android system indefinitely. Simply making the `ExpoNotificationSchedulerService` a POJO will make `ScheduledAlarmReceiver` not try to enqueue work to another service, but execute it immediately.

# How

Renamed `ExpoNotificationSchedulerService` to `NotificationSchedulingHelper` and made it a POJO with only the `enqueue*` methods being public and available.

# Test Plan

I've verified that tests related to scheduling pass in `test-suite`.
sjchmiela added a commit that referenced this pull request Oct 15, 2020
… flow (#10680)

# Why

Preparation to make `expo-notifications-categories-delegate` PR easier to review. See 42ffff4.

# How

Reverted changes made in #9287 so that categories managing uses `ResultReceiver`.

# Test Plan

I've verified that all relevant tests pass with `test-suite`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants