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

Background push notification processing #3877

Merged
merged 37 commits into from
Sep 1, 2021

Conversation

roryabraham
Copy link
Contributor

@roryabraham roryabraham commented Jul 2, 2021

Details

This pull request makes it so that push notifications received when the Android app is closed will update Onyx, within the bounds of certain OS restrictions. This is important to achieve a background data trickle, and update the mobile app with chat data as frequently as possible.

On Android, push notifications received when the app is closed will wake a headless JS process (that lives in the UrbanAirship RN sdk) and execute a JS callback. However, it will not mount any RN components, so the push notification callbacks must be registered outside of any React lifecycle. Because the callback is dependent on the Onyx store, we must also initialize that outside of any React lifecycle. Fortunately, that works just fine for our application 😁

Fixed Issues

$ (partial) https://github.com/Expensify/Expensify/issues/158830

Tests

This is a bit of a doozy to test. It will be native-only, with only regression testing on other platforms.

Get a prod build running – Android

  1. Follow the instructions in this SO to generate a signing key and provide it to the app through build.gradle.

  2. Open android/app/src/main/assets/airshipconfig.properties. Swap out the productionAppKey and productionAppSecret with the developmentAppKey and developmentAppSecret.

    image

  3. Run npm i

  4. Run react-native start --reset-cache. Kill this command once you see the cache has been cleared.

  5. Run cd android && ./gradlew clean

  6. Open your Android emulator and make sure to fully uninstall any Expensify.cash app on there.

  7. Run npm run android -- --variant=release.

  8. Once that runs, verify that you do not see any logs in the metro window that opens. Kill the metro window, and verify that you can still view the app. Close the app (hit the square button and swipe up), then reopen it. It should still work.

Get a prod build running – iOS

  1. This requires a physical iPhone for testing.
  2. Run through the steps in this SO to get set up to run the app on a physical iPhone device.
  3. Run npm i -g ios-deploy
  4. Plug your device into your computer. Click "trust" when prompted.
  5. Open XCode, go to the Expensify.cash project configurations, click Sigining and Capabilities, and click "Enable Automatic". This will make some temporary changes that must not be committed, but for me are always necessary to get the app to launch on a physical device.
  6. Finally, run npm run ios -- --configuration Release --device "Your Device Name"

Test push notifications (both platforms)

  1. Tail your VM logs for [Report] Handled event sent by Airship. This will indicate that the push notification callback executed.

  2. Run npm run web (you pick)

  3. Sign into account A on the mobile app. Sign into account B on the web app. These should be accounts on your VM, not prod accounts.

  4. Send a message from account B to account A. It should arrive normally.

  5. Send a reply from account A to account B. It should arrive normally.

  6. As user A, open a different chat, and background the app.

  7. Send a message from B to A. A push notification should appear, and a log line demonstrating that the callback occurred should also appear.

  8. Put the emulator/device on airplane mode.

  9. Double-tap the notification. It should open E.cash to the correct chat, and the message should be available.

  10. Take the emulator/device off airplane mode.

  11. (android only) Open the emulator settings, then go to Security -> Screen Lock -> Swipe.

    image

  12. Hit the power button on the emulator menu (or lock button on the iPhone) to lock the emulator/device:

    image

  13. Send a message from B to A. A push notification should appear, and a log line demonstrating that the callback occurred should also appear.

  14. Put the emulator/device on airplane mode.

  15. Double-tap the notification. It should open E.cash to the correct chat, and the new message should be available.

  16. Hit the square button and swipe up to close E.cash entirely.

  17. Send a message from B to A. A push notification should appear, and a log line demonstrating that the callback occurred should also appear.

  18. Put the emulator/device on airplane mode.

  19. Double-tap the notification. It should open E.cash to the correct chat, and the new message should be available.

QA Steps

  1. Sign into account A on the Android app. Sign into account B on the web app.
  2. Send a message from account B to account A. It should arrive normally.
  3. Send a reply from account A to account B. It should arrive normally.
  4. As user A, open a different chat, and background the app.
  5. Send a message from B to A. A push notification should appear.
  6. After about 10 seconds, put the emulator on airplane mode.
  7. Double-tap the notification. It should open E.cash to the correct chat, and the message should be available.
  8. Take the emulator off airplane mode.
  9. Open the android settings, then go to Security -> Screen Lock -> Swipe (doesn't have to be swipe – just make sure you can see a lock screen on the device).
  10. Lock the Android device.
  11. Send a message from B to A. A push notification should appear.
  12. After about 10 seconds, put the emulator on airplane mode.
  13. Double-tap the notification. It should open E.cash to the correct chat, and the new message should be available.
  14. Hit the square button and swipe up to close E.cash entirely.
  15. Send a message from B to A. A push notification should appear, and a log line demonstrating that the callback occurred should also appear.
  16. After about 10 seconds, put the emulator on airplane mode.
  17. Double-tap the notification. It should open E.cash to the correct chat, and the new message should be available.

Tested On

  • Web
  • Mobile Web
  • Desktop
  • iOS
  • Android

Screenshots

Web

Mobile Web

Desktop

iOS

Android

....Will need to gather screen recordings after the long weekend.

@roryabraham roryabraham requested a review from a team July 2, 2021 23:26
@roryabraham roryabraham self-assigned this Jul 2, 2021
@roryabraham roryabraham changed the title Background push notifications v2 Android background push notifications Jul 2, 2021
@MelvinBot MelvinBot requested review from nickmurray47 and removed request for a team July 2, 2021 23:26
@roryabraham roryabraham changed the title Android background push notifications [HOLD] Android background push notifications Jul 2, 2021
@roryabraham
Copy link
Contributor Author

Going to need to HOLD on a Web-E PR to make sure that we're passing 'delivery_priority' => 'high' along with Android push notifications (only for report comments for now).

@roryabraham roryabraham changed the title [HOLD] Android background push notifications [HOLD] Background push notification processing Jul 6, 2021
@nickmurray47
Copy link
Contributor

@roryabraham did you want an early review on this or should I wait until the HOLD comes off?

@roryabraham roryabraham requested a review from a team as a code owner July 7, 2021 01:12
@MelvinBot MelvinBot requested review from Luke9389 and removed request for a team July 7, 2021 01:12
@roryabraham
Copy link
Contributor Author

@nickmurray47 Feel free to review when you're ready! Just holding on the deployment of the API changes.

@roryabraham roryabraham removed the request for review from Luke9389 July 7, 2021 01:55
@roryabraham
Copy link
Contributor Author

Not sure why PullerBear got a bit trigger-happy there @Luke9389, feel free to ignore.

@roryabraham
Copy link
Contributor Author

Hooray, I was able to observe the iOS app being awoken and updating Onyx when the app was completely closed 🎉. It certainly does not happen consistently, even when the app is only backgrounded and not closed, but there is nothing we can do to side-step the OS limitations on content_available push notifications.

@nickmurray47
Copy link
Contributor

Going through the testing steps now

nickmurray47
nickmurray47 previously approved these changes Jul 8, 2021
Copy link
Contributor

@nickmurray47 nickmurray47 left a comment

Choose a reason for hiding this comment

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

Had a couple questions, but the code LGTM as far as I can tell. Still running through the testing steps.

src/libs/Notification/PushNotification/index.native.js Outdated Show resolved Hide resolved
src/libs/actions/Report.js Show resolved Hide resolved
@roryabraham
Copy link
Contributor Author

@nickmurray47 Let me know if you have any more questions here

@roryabraham
Copy link
Contributor Author

@nickmurray47 If you are running through the testing steps, you'll need to checkout this PR on Web-E for it to work.

@roryabraham
Copy link
Contributor Author

Also, @nickmurray47 I added some testing steps for iOS, since this fixes some push notification issues on iOS as well.

@nickmurray47
Copy link
Contributor

If you are running through the testing steps, you'll need to checkout this PR on Web-E for it to work.

Figured I was missing something like this! Will test the new iOS steps today

@roryabraham
Copy link
Contributor Author

cc @AndrewGable if you're interested

@roryabraham
Copy link
Contributor Author

Sorry to muddy up the git history, those last changes were meant to go to another PR. They've been removed from this one.

@Julesssss
Copy link
Contributor

Sorry to hold up this PR -- I would like to review and test this properly but have just not had the time to get to additional reviews today.

@nickmurray47
Copy link
Contributor

Re-ran through all the test steps and now navigating to the exact chat from a notification works great!

One thing I just noticed (and not sure if this is by design) but it seems like the notification only pops up when I've fully closed the app. If I just switch to a different chat, background it, and receive a new message then no notification comes up.

@marcaaron
Copy link
Contributor

One thing I just noticed (and not sure if this is by design) but it seems like the notification only pops up when I've fully closed the app. If I just switch to a different chat, background it, and receive a new message then no notification comes up.

I experienced something similar but it was more like... a lag or something where it just took several seconds for the notification to appear.

@nickmurray47
Copy link
Contributor

I experienced something similar but it was more like... a lag or something where it just took several seconds for the notification to appear.

Cool, wanted to make a note of that. The code looks good to me, very nicely done.

@roryabraham
Copy link
Contributor Author

roryabraham commented Aug 31, 2021

One thing I just noticed (and not sure if this is by design) but it seems like the notification only pops up when I've fully closed the app. If I just switch to a different chat, background it, and receive a new message then no notification comes up.

Thanks for retesting @nickmurray47! This sounds like it could be a separate issue ... unless you change some of the code, Web-Expensify will send a silent push notification if it still thinks you're connected via pusher. Might be addressed over here.

@Julesssss
Copy link
Contributor

This sounds like it could be a separate issue ... unless you change some of the code, Web-Expensify will send a silent push notification if it still thinks you're connected via pusher. Might be addressed over here.

Yeah, that will be solved by this issue Rory linked.

Comment on lines +28 to +30
globals: {
__DEV__: 'readonly',
},
Copy link
Contributor

Choose a reason for hiding this comment

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

What is this for?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

__DEV__ is a global variable in RN, and this tells ESLint that so that we can remove some unnecessary ESLint suppressions. Not too closely related to this PR, just a bit of housekeeping.

@@ -26,6 +26,7 @@
<key>CFBundleURLSchemes</key>
<array>
<string>expensify-cash</string>
Copy link
Contributor

@Julesssss Julesssss Sep 1, 2021

Choose a reason for hiding this comment

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

Do we still need the old URLScheme? Are you aware of any code that still uses this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not aware of any code that still uses this, but I know we have some code that links back-and-forth between OldDot and NewDot and I don't want to break anything in this PR by removing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can create a FP follow-up to remove this and any usages across all our repos (probably just E/App and Web-Expensify).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

@Julesssss Julesssss left a comment

Choose a reason for hiding this comment

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

Cool, this is working really well! (I tested an Android physical device)

I'm not sure if this is helpful at all, but saw some comments about headless tasks... the whole headless JS thing is necessary because Android is so protective of UI threads. The only way to render anything on Android is via a UI thread, and ignoring some exceptions we are not granted access to these threads outside of an Activity. Services allow us to run code in the background but don't provide access to the UI thread and are restricted by the OS, hence why ReactNative Headless tasks extend from Service, doesn't have access to the App context, and cannot make any UI changes.

@roryabraham roryabraham merged commit 4e8b6bf into main Sep 1, 2021
@roryabraham roryabraham deleted the Rory-BackgroundPushNotificationsV2 branch September 1, 2021 17:34
@OSBotify
Copy link
Contributor

OSBotify commented Sep 1, 2021

✋ This PR was not deployed to staging yet because QA is ongoing. It will be automatically deployed to staging after the next production release.

@OSBotify
Copy link
Contributor

OSBotify commented Sep 2, 2021

🚀 Deployed to staging by @roryabraham in version: 1.0.91-1 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@isagoico
Copy link

isagoico commented Sep 2, 2021

This issue is failing this PR #5022

@roryabraham
Copy link
Contributor Author

roryabraham commented Sep 2, 2021

@isagoico Can we please retest this PR with the following testing steps:

  1. Sign into account A on the Android app. Sign into account B on the web app.
  2. Send a message from account B to account A. It should arrive normally.
  3. Send a reply from account A to account B. It should arrive normally.
  4. As user A, open a different chat, and background the app.
  5. Wait for 5 minutes.
  6. Send a message from B to A. A push notification should appear.
  7. After about 10 seconds, put the emulator on airplane mode.
  8. Double-tap the notification. It should open E.cash to the correct chat, and the message should be available.
  9. Take the emulator off airplane mode.
  10. Open the android settings, then go to Security -> Screen Lock -> Swipe (doesn't have to be swipe – just make sure you can see a lock screen on the device).
  11. Lock the Android device.
  12. Wait for 5 minutes.
  13. Send a message from B to A. A push notification should appear.
  14. After about 10 seconds, put the emulator on airplane mode.
  15. Double-tap the notification. It should open E.cash to the correct chat, and the new message should be available.
  16. Hit the square button and swipe up to close E.cash entirely.
  17. Wait for 5 minutes.
  18. Send a message from B to A. A push notification should appear, and a log line demonstrating that the callback occurred should also appear.
  19. After about 10 seconds, put the emulator on airplane mode.
  20. Double-tap the notification. It should open E.cash to the correct chat, and the new message should be available.

@isagoico
Copy link

isagoico commented Sep 3, 2021

Just tested this on Android with the new steps and it was a pass 🎉 Checking it off

@OSBotify
Copy link
Contributor

OSBotify commented Sep 3, 2021

🚀 Deployed to production by @roryabraham in version: 1.0.92-0 🚀

platform result
🤖 android 🤖 success ✅
🖥 desktop 🖥 success ✅
🍎 iOS 🍎 success ✅
🕸 web 🕸 success ✅

@roryabraham
Copy link
Contributor Author

@isagoico, when you get a chance can you please confirm that the testing steps from this PR are included in our regression 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.

7 participants