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
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
7 changes: 7 additions & 0 deletions OneSignalSDK/onesignal/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,16 @@
android:name="com.onesignal.NotificationOpenedReceiver"
android:noHistory="true"
android:excludeFromRecents="true"
android:taskAffinity=""
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="true" />

<activity
android:name="com.onesignal.NotificationOpenedReceiverAndroid22AndOlder"
android:noHistory="true"
android:excludeFromRecents="true"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="true" />
</application>

<!-- NOTE: See release version for tags with placeholders -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,66 @@ package com.onesignal
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.annotation.RequiresApi

class GenerateNotificationOpenIntent(
private val context: Context,
private val intent: Intent?,
private val startApp: Boolean
) {

private val notificationOpenedClass: Class<*> = NotificationOpenedReceiver::class.java
private val notificationOpenedClassAndroid23Plus: Class<*> = NotificationOpenedReceiver::class.java
private val notificationOpenedClassAndroid22AndOlder: Class<*> = NotificationOpenedReceiverAndroid22AndOlder::class.java

fun getNewBaseIntent(
notificationId: Int,
): Intent {
val intent =
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M)
getNewBaseIntentAndroidAPI23Plus()
else
getNewBaseIntentAndroidAPI22AndOlder()

return intent
.putExtra(
GenerateNotification.BUNDLE_KEY_ANDROID_NOTIFICATION_ID,
notificationId
)
// We use SINGLE_TOP and CLEAR_TOP as we don't want more than one OneSignal invisible click
// tracking Activity instance around.
var intentFlags =
.addFlags(
Intent.FLAG_ACTIVITY_SINGLE_TOP or
Intent.FLAG_ACTIVITY_CLEAR_TOP
if (!startApp) {
// If we don't want the app to launch we put OneSignal's invisible click tracking Activity on it's own task
// so it doesn't resume an existing one once it closes.
intentFlags =
intentFlags or (
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
)
}
)
}

@RequiresApi(android.os.Build.VERSION_CODES.M)
private fun getNewBaseIntentAndroidAPI23Plus(): Intent {
return Intent(
context,
notificationOpenedClass
notificationOpenedClassAndroid23Plus
)
.putExtra(
GenerateNotification.BUNDLE_KEY_ANDROID_NOTIFICATION_ID,
notificationId
}

// See NotificationOpenedReceiverAndroid22AndOlder.kt for details
@Deprecated("Use getNewBaseIntentAndroidAPI23Plus instead for Android 6+")
private fun getNewBaseIntentAndroidAPI22AndOlder(): Intent {
val intent = Intent(
context,
notificationOpenedClassAndroid22AndOlder
)
.addFlags(intentFlags)

if (getIntentVisible() == null) {
// If we don't show a visible Activity put OneSignal's invisible click tracking
// Activity on it's own task so it doesn't resume an existing one once it closes.
intent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_MULTIPLE_TASK or
Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET
)
}

return intent
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Modified MIT License
*
* Copyright 2021 OneSignal
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* 1. The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 2. All copies of substantial portions of the Software may only be used in connection
* with services provided by OneSignal.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.onesignal

class NotificationOpenedReceiver : NotificationOpenedReceiverBase()
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Modified MIT License
*
* Copyright 2021 OneSignal
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* 1. The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 2. All copies of substantial portions of the Software may only be used in connection
* with services provided by OneSignal.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.onesignal

/**
* This is the same as NotificationOpenedReceiver expect it doesn't contain
* android:taskAffinity="" in it's AndroidManifest.xml entry. This is to
* account for the resume behavior not working on Android API 22 and older.
*
* In Android 5.x and older, android:taskAffinity="" starts a new task as
* expected, however the allowTaskReparenting behavior does not happen which
* results in the app not resuming, when using reverse Activity trampoline.
* Oddly enough cold starts of the app were not a problem.
*/
class NotificationOpenedReceiverAndroid22AndOlder : NotificationOpenedReceiverBase()
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* Modified MIT License
*
* Copyright 2017 OneSignal
* Copyright 2021 OneSignal
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
Expand All @@ -24,27 +24,22 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.onesignal

package com.onesignal;
import android.app.Activity
import android.content.Intent
import android.os.Bundle

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

public class NotificationOpenedReceiver extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
NotificationOpenedProcessor.processFromContext(this, getIntent());
finish();
}

@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
NotificationOpenedProcessor.processFromContext(this, getIntent());
finish();
}
abstract class NotificationOpenedReceiverBase : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
NotificationOpenedProcessor.processFromContext(this, intent)
finish()
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
NotificationOpenedProcessor.processFromContext(this, getIntent())
finish()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,21 @@ public void shouldSetButtonsCorrectly() throws Exception {
assertEquals("id1", new JSONObject(json_data).optString(BUNDLE_KEY_ACTION_ID));
}

@Test
@Config(sdk = 23, shadows = { ShadowGenerateNotification.class })
public void shouldUseCorrectActivityForAndroid23Plus() throws Exception {
NotificationBundleProcessor_ProcessFromFCMIntentService(blankActivity, getBaseNotifBundle());
threadAndTaskWait();

Intent[] intents = lastNotificationIntents();
assertEquals("android.intent.action.MAIN", intents[0].getAction());
assertEquals(
com.onesignal.NotificationOpenedReceiver.class.getName(),
intents[1].getComponent().getClassName()
);
assertEquals(2, intents.length);
}

@Test
@Config(shadows = { ShadowGenerateNotification.class })
public void shouldSetContentIntentForLaunchURL() throws Exception {
Expand Down Expand Up @@ -1180,7 +1195,7 @@ private void generateNotificationWithLaunchURL() throws Exception {
}

private void assertNotificationOpenedReceiver(@NonNull Intent intent) {
assertEquals("com.onesignal.NotificationOpenedReceiver", intent.getComponent().getClassName());
assertEquals(com.onesignal.NotificationOpenedReceiverAndroid22AndOlder.class.getName(), intent.getComponent().getClassName());
}

private void assertOpenMainActivityIntent(@NonNull Intent intent) {
Expand Down