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

Fix outside package target activities (Launch URL) not opening when notification is tapped on #1469

Merged
merged 6 commits into from
Nov 2, 2021

Conversation

jkasten2
Copy link
Member

@jkasten2 jkasten2 commented Oct 26, 2021

Description

One Line Summary

Fix notification open with Launch URL not showing any Activities, this was a regression bug introduced in PR #1377.

Details

Motivation

Notification Launch URL is an active supported feature used by OneSignal customers so ensuring it works correctly is high priority.

Scope

The main goal is to fix Launch URL not working but the code change in this PR effects all notification opens. The "opens" noted here is scoped to how Activities are shown to the user. This includes the Activity flags used to start Activities and the backstace it generates.
No changes to how OneSignal consumes or generates Intent data.

Why the bug was happening?

The issue was due to an Activity backstack not being built correctly. Both NotificationOpenedReceiver and the target Activity were launched with FLAG_ACTIVITY_NEW_TASK however this means when NotificationOpenedReceiver is finished it does not bring up the target since it is on a different task. The Android flag allowTaskReparenting allows this to happen, however this can effect the backstack of the main task which we don't want to do.

How the bug was fixed?

Setting android:taskAffinity sets FLAG_ACTIVITY_NEW_TASK and allowTaskReparenting for us. By setting the value to "" (empty string) it prevents any side effects on existing tasks as it means it has no affinity to associate it with. This allowed us to clean up some runtime logic to add FLAG_ACTIVITY_NEW_TASK and others we no longer need now.

A caveat is that on Android 5.1 and older android:taskAffinity="" doesn't give us the allowTaskReparenting behavior, nor does setting it explicitly work. To account for this an Activity named NotificationOpenedReceiverAndroid22AndOlder was created without the android:taskAffinity parameter.

Related additional information

Tasks and the back stack

Basic understand of this topic is recommend

What android:taskAffinity="" does

By default, all activities in an application have the same affinity. You can set this attribute to group them differently, and even place activities defined in different applications within the same task. To specify that the activity does not have an affinity for any task, set it to an empty string.

Source: https://developer.android.com/guide/topics/manifest/activity-element#aff

Notification Trampolines

Background on what "Notification Trampolines" are and how a "Reverse Activity Trampoline" works.

Testing

Unit testing

Added test to ensure correct Activity is used for newer vs older versions of Android.
No existing exist unit tests to ensure correct Activity is infocus or the backstack is built correctly. Robolectric doesn't have fakes / mocking to simulate to this level.

Manual testing

Each scenario was tested with the following devices:

  • ⚠️Can't test older Android versions, due to know TLS1.2 issue
  • ✅Android 5.0 Emulator
  • ✅Android 5.1 Emulator
  • ✅Android 6.0 Emulator
  • ✅Android 7.1 Emulator
  • ✅Android 8.1 Emulator
  • ✅Android 9 LG G7
  • ✅Android 9 Amazon Fire 7 (KFMUWI)
  • ✅Android 10 Emulator
  • ✅Android 11 Emulator
  • ✅Android 12 Emulator

Default notification

  • App Foreground
  • App Background - by pressing back button
  • App Background - by pressing home button
  • App not running - Task still in recent list, resumes app's task correctly
  • App not running - Recent list cleared, app cold starts new task

HTTPS URL notification

  • App Foreground
  • App Background - by pressing home button
  • App not running - Recent list cleared, app cold starts new task

com.onesignal.NotificationOpened.DEFAULT set to DISABLE

  • App Foreground
  • App Background - by pressing home button
  • App not running - swiped away app

Data URL scheme - appname://test

Tested by adding the following <intent-filter> entries for the following Activities in the example app's AndroidManifest.xml.

<activity
    android:name=".activity.SplashActivity"
    android:exported="true">
  <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
  <intent-filter>
      <action android:name="android.intent.action.VIEW"/>
      <category android:name="android.intent.category.DEFAULT"/>
      <category android:name="android.intent.category.BROWSABLE"/>
      <data android:scheme="osexamplesplash"/>
  </intent-filter>
</activity>

<activity
    android:name=".activity.MainActivity"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <category android:name="android.intent.category.BROWSABLE"/>
        <data android:scheme="osexample"/>
    </intent-filter>
</activity>

Each scenario below includes testing twice, once with osexamplesplash:// another with osexample://.

  • App Foreground
  • App Background - by pressing home button
  • App Background - App swiped away
  • App not running - Recent list cleared, app cold starts new task

Affected code checklist

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
  • Outcomes
  • Sessions
  • REST API requests
  • Public API changes

Checklist

Overview

  • I have filled out all REPLACE sections above
  • PR does one thing
    • If it is hard to explain how any codes changes are related to each other then it most likely needs to be more than one PR
  • Public API changes are intended and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

Final pass

  • Code is as readable as possible.
    • Simplify with less code, followed by splitting up code into well named functions and variables, followed by adding comments to the code.
  • I have reviewed this PR myself, ensuring it meets each checklist item
    • WIP (Work In Progress) is ok, but explain what is still in progress and what you would like feedback on. Start the PR title with "WIP" to indicate this.

This change is Reviewable

This fix includes any launch URLs set to open the browser or another app

The issue was due to an Activity backstack not being built correctly.
Both `NotificationOpenedReceiver` and the target `Activity` were
launched with `FLAG_ACTIVITY_NEW_TASK` however this means when
`NotificationOpenedReceiver` is finished it does not bring up the target
since it is on a different task. The Android flag `allowTaskReparenting`
allows this to happen, however this can effect the backstack of the main
task which we don't want to do.

Setting `android:taskAffinity` does `FLAG_ACTIVITY_NEW_TASK` and
`allowTaskReparenting` for us. By setting the value to "" (empty string)
it prevents any side effects on existing tasks as it means it has no
affinity to associate it with. This allowed us to clean up some runtime
logic to add `FLAG_ACTIVITY_NEW_TASK` and others we no longer need now.
@jkasten2 jkasten2 added the WIP Work In Progress label Oct 26, 2021
App would not resume on Android 5 after the `taskAffinity=""` addition
added in the last commit. This seems to be an issue with
allowTaskReparenting not working in this case. Even explicitly enabling
 it did not work.

 `taskAffinity` is required for newer vesions of Android to correctly
 start a new task and to reparent it to the correct task. This is why
 two different implementations had to be added.
This is to flag that these should be cleaned up when support for
Android API 22 and older is dropped.
Ran the auto convert in Android Studio and updated comment in code
@jkasten2 jkasten2 force-pushed the fix/outside_package_target_activities branch from 9a83d47 to 00f1367 Compare October 27, 2021 21:50
Add test to ensure correct Activity is for Notification opens
when device is running Android API 23 (Android 6) or newer.
@jkasten2 jkasten2 changed the title WIP - Fix outside package target activities not opening Fix outside package target activities not opening Oct 27, 2021
@jkasten2 jkasten2 removed the WIP Work In Progress label Oct 27, 2021
@jkasten2 jkasten2 changed the title Fix outside package target activities not opening Fix outside package target activities (Launch URL) not opening when notification is tapped on Oct 27, 2021
@jkasten2 jkasten2 requested a review from a team October 28, 2021 21:21
Copy link
Contributor

@emawby emawby left a comment

Choose a reason for hiding this comment

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

Reviewed 5 of 5 files at r1.
Reviewable status: all files reviewed (commit messages unreviewed), 1 unresolved discussion (waiting on @Jeasmine, @jkasten2, and @nan-li)


OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt, line 11 at r1 (raw file):

    private val context: Context,
    private val intent: Intent?,
    private val startApp: Boolean

Are we using this boolean anymore?

* results in the app not resuming, when using reverse Activity trampoline.
* Oddly enough cold starts of the app were not a problem.
*/
class NotificationOpenedReceiverAndroid22AndOlder : Activity() {
Copy link
Contributor

Choose a reason for hiding this comment

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

will it make sense to have a single Base NotificationOpenedReceiver and both NotificationOpenedReceiver and NotificationOpenedReceiverAndroid22AndOlder implement it?

Copy link
Member Author

@jkasten2 jkasten2 left a comment

Choose a reason for hiding this comment

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

Reviewable status: all files reviewed (commit messages unreviewed), 2 unresolved discussions (waiting on @emawby, @Jeasmine, and @nan-li)


OneSignalSDK/onesignal/src/main/java/com/onesignal/GenerateNotificationOpenIntent.kt, line 11 at r1 (raw file):

Previously, emawby (Elliot Mawby) wrote…

Are we using this boolean anymore?

Yes, the only place it gets used now is at the top of getIntentAppOpen.


OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedReceiverAndroid22AndOlder.kt, line 43 at r1 (raw file):

Previously, Jeasmine (JNahui) wrote…

will it make sense to have a single Base NotificationOpenedReceiver and both NotificationOpenedReceiver and NotificationOpenedReceiverAndroid22AndOlder implement it?

ah yes, I'll do that. It will de-dup a few lines of code and prevent missing the other class when updating any of the logic in it.

Created abstract NotificationOpenedReceiverBase to share code between
both activities. It is very unlikly the code would ever be different
between them and prevents possible future mistakes of editing only one.
Copy link
Member Author

@jkasten2 jkasten2 left a comment

Choose a reason for hiding this comment

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

Reviewable status: 3 of 6 files reviewed, 2 unresolved discussions (waiting on @emawby, @Jeasmine, and @nan-li)


OneSignalSDK/onesignal/src/main/java/com/onesignal/NotificationOpenedReceiverAndroid22AndOlder.kt, line 43 at r1 (raw file):

Previously, jkasten2 (Josh Kasten) wrote…

ah yes, I'll do that. It will de-dup a few lines of code and prevent missing the other class when updating any of the logic in it.

I addressed this in commit 17f6340

@jkasten2 jkasten2 merged commit bbd6545 into main Nov 2, 2021
@jkasten2 jkasten2 deleted the fix/outside_package_target_activities branch November 2, 2021 18:45
@jkasten2 jkasten2 mentioned this pull request Nov 5, 2021
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.

3 participants