Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ internal object AEPPushNotificationBuilder {
)
.setNotificationDeleteAction(context, trackerActivityClass)

// API21 specific fixes
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
// intent handling fix, see MOB-21261 for more info
builder.setOnlyAlertOnce(true)
// heads up display fix, see MOB-21447 for more info
builder.setCustomHeadsUpContentView(expandedLayout)
}

// API22 and 23 heads up display fix, see MOB-21447 for more info
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.M || Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1) {
builder.setCustomHeadsUpContentView(smallLayout)
}

// if not from intent, set custom sound, note this applies to API 25 and lower only as
// API 26 and up set the sound on the notification channel
if (!pushTemplate.isFromIntent) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.os.Build
import android.widget.RemoteViews
import androidx.annotation.VisibleForTesting
import androidx.core.app.NotificationCompat
Expand Down Expand Up @@ -47,7 +48,14 @@ internal object BasicNotificationBuilder {
Log.trace(LOG_TAG, SELF_TAG, "Building a basic template push notification.")
val packageName = context.packageName
val smallLayout = RemoteViews(packageName, R.layout.push_template_collapsed)
val expandedLayout = RemoteViews(packageName, R.layout.push_template_expanded)
var expandedLayout = RemoteViews(packageName, R.layout.push_template_expanded)

// API23 and below have a limited notification display area. the notification elements
// must use a smaller area to fix buttons not showing on expanded notification.
// see MOB-21262 for more info
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.M) {
expandedLayout = RemoteViews(packageName, R.layout.push_template_expanded_api23)
}

val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import androidx.core.app.RemoteInput
Expand Down Expand Up @@ -44,6 +45,11 @@ internal object InputBoxNotificationBuilder {
trackerActivityClass: Class<out Activity>?,
broadcastReceiverClass: Class<out BroadcastReceiver>?
): NotificationCompat.Builder {

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
throw NotificationConstructionFailedException("Input box push notification on devices below Android N is not supported.")
}

Log.trace(LOG_TAG, SELF_TAG, "Building an input box template push notification.")
val packageName = context.packageName
val smallLayout = RemoteViews(packageName, R.layout.push_template_collapsed)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/basic_expanded_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:clipChildren="false"
android:orientation="vertical"
android:theme="@style/DayNightTheme">

<ImageView
android:id="@+id/large_icon"
android:layout_height="@dimen/large_icon_height"
android:layout_width="@dimen/large_icon_width"
android:layout_alignParentRight="true" />

<TextView
android:id="@+id/notification_title"
style="@style/Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
android:textAlignment="viewStart"
android:layout_toLeftOf="@+id/large_icon"/>

<TextView
android:id="@+id/notification_body_expanded"
style="@style/Body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/notification_title"
android:layout_gravity="start"
android:textAlignment="viewStart"
android:layout_toLeftOf="@+id/large_icon"/>

<ImageView
android:id="@+id/expanded_template_image"
android:layout_width="match_parent"
android:layout_height="@dimen/basic_expanded_image_height_api23"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:layout_below="@id/notification_body_expanded"/>
</RelativeLayout>
1 change: 1 addition & 0 deletions code/notificationbuilder/src/main/res/values/dimens.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
<resources>
<dimen name="large_icon_height">35dp</dimen>
<dimen name="large_icon_width">35dp</dimen>
<dimen name="basic_expanded_image_height_api23">160dp</dimen>
<dimen name="basic_expanded_image_height">200dp</dimen>
<dimen name="carousel_item_layout_height">200dp</dimen>
<dimen name="carousel_item_layout_width">250dp</dimen>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ import com.adobe.marketing.mobile.notificationbuilder.internal.util.IntentData
import com.adobe.marketing.mobile.notificationbuilder.internal.util.MapData
import io.mockk.unmockkAll
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertFalse
import junit.framework.TestCase.assertNotNull
import junit.framework.TestCase.assertNull
import junit.framework.TestCase.assertTrue
import org.junit.After
import org.junit.Assert.assertArrayEquals
import org.junit.Assert.assertNotEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
Expand Down Expand Up @@ -126,6 +129,97 @@ class AEPPushNotificationBuilderTest {
verifyNotificationViewDataAndColors(pushTemplate)
}

@Config(sdk = [21])
@Test
fun `construct should set alert only once for API level below 22`() {
val pushTemplate = BasicPushTemplate(MapData(dataMap))
val notification =
AEPPushNotificationBuilder.construct(
context,
pushTemplate,
CHANNEL_ID_TO_USE,
trackerActivityClass,
smallLayout,
expandedLayout,
CONTAINER_LAYOUT_VIEW_ID
).build()
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
assertTrue(NotificationCompat.getOnlyAlertOnce(notification))
}
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
assertFalse(NotificationCompat.getOnlyAlertOnce(notification))
}
}

@Config(sdk = [21])
@Test
fun `construct should set the expanded layout as the custom heads up content view for API level below 22`() {
val pushTemplate = BasicPushTemplate(MapData(dataMap))
val notificationBuilder =
AEPPushNotificationBuilder.construct(
context,
pushTemplate,
CHANNEL_ID_TO_USE,
trackerActivityClass,
smallLayout,
expandedLayout,
CONTAINER_LAYOUT_VIEW_ID
)
assertEquals(expandedLayout, notificationBuilder.headsUpContentView)
}

@Config(sdk = [22])
@Test
fun `construct should set the small layout as the custom heads up content view for API 22`() {
val pushTemplate = BasicPushTemplate(MapData(dataMap))
val notificationBuilder =
AEPPushNotificationBuilder.construct(
context,
pushTemplate,
CHANNEL_ID_TO_USE,
trackerActivityClass,
smallLayout,
expandedLayout,
CONTAINER_LAYOUT_VIEW_ID
)
assertEquals(smallLayout, notificationBuilder.headsUpContentView)
}

@Config(sdk = [23])
@Test
fun `construct should set the small layout as the custom heads up content view for API 23`() {
val pushTemplate = BasicPushTemplate(MapData(dataMap))
val notificationBuilder =
AEPPushNotificationBuilder.construct(
context,
pushTemplate,
CHANNEL_ID_TO_USE,
trackerActivityClass,
smallLayout,
expandedLayout,
CONTAINER_LAYOUT_VIEW_ID
)
assertEquals(smallLayout, notificationBuilder.headsUpContentView)
}

@Config(sdk = [24])
@Test
fun `construct should not set a custom heads up content view for API 24 or higher`() {
val pushTemplate = BasicPushTemplate(MapData(dataMap))
val notificationBuilder =
AEPPushNotificationBuilder.construct(
context,
pushTemplate,
CHANNEL_ID_TO_USE,
trackerActivityClass,
smallLayout,
expandedLayout,
CONTAINER_LAYOUT_VIEW_ID
)
assertNotEquals(smallLayout, notificationBuilder.headsUpContentView)
assertNotEquals(expandedLayout, notificationBuilder.headsUpContentView)
}

@Test
fun `construct should not set notification sound if pushTemplate sound is invalid`() {
dataMap.replaceValueInMap(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ import com.adobe.marketing.mobile.notificationbuilder.internal.templates.MockAEP
import com.adobe.marketing.mobile.notificationbuilder.internal.templates.provideMockedBasicPushTemplateWithAllKeys
import com.adobe.marketing.mobile.notificationbuilder.internal.templates.provideMockedBasicPushTemplateWithRequiredData
import com.adobe.marketing.mobile.notificationbuilder.internal.util.MapData
import com.google.common.base.Verify.verify
import io.mockk.every
import io.mockk.mockkConstructor
import io.mockk.mockkObject
Expand Down Expand Up @@ -102,6 +101,19 @@ class BasicNotificationBuilderTest {
verify(exactly = 1) { any<RemoteViews>().setRemoteViewImage(MOCKED_IMAGE_URI, R.id.expanded_template_image) }
}

@Config(sdk = [23])
@Test
fun `construct should use the api23 expanded layout for API level below 24`() {
val pushTemplate = provideMockedBasicPushTemplateWithAllKeys()
val notificationBuilder = BasicNotificationBuilder.construct(
context,
pushTemplate,
trackerActivityClass,
broadcastReceiverClass
)
assertEquals(R.layout.push_template_expanded_api23, notificationBuilder.bigContentView.layoutId)
}

@Test
fun `construct should set parameters for notification builder properly`() {
val pushTemplate = provideMockedBasicPushTemplateWithAllKeys()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ package com.adobe.marketing.mobile.notificationbuilder.internal.builders
import android.app.Activity
import android.content.BroadcastReceiver
import android.content.Context
import android.os.Build
import android.widget.RemoteViews
import androidx.core.app.NotificationCompat
import com.adobe.marketing.mobile.notificationbuilder.NotificationConstructionFailedException
import com.adobe.marketing.mobile.notificationbuilder.PushTemplateConstants
import com.adobe.marketing.mobile.notificationbuilder.R
import com.adobe.marketing.mobile.notificationbuilder.internal.extensions.setRemoteImage
Expand All @@ -35,7 +37,6 @@ import com.adobe.marketing.mobile.notificationbuilder.internal.templates.provide
import com.adobe.marketing.mobile.notificationbuilder.internal.templates.removeKeysFromMap
import com.adobe.marketing.mobile.notificationbuilder.internal.templates.replaceValueInMap
import com.adobe.marketing.mobile.notificationbuilder.internal.util.MapData
import com.google.common.base.Verify.verify
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
Expand All @@ -52,6 +53,7 @@ import org.robolectric.RobolectricTestRunner
import org.robolectric.RuntimeEnvironment
import org.robolectric.Shadows
import org.robolectric.annotation.Config
import org.robolectric.util.ReflectionHelpers
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
Expand Down Expand Up @@ -93,6 +95,19 @@ class InputBoxNotificationBuilderTest {
assertEquals(NotificationCompat.Builder::class.java, notificationBuilder.javaClass)
}

@Test(expected = NotificationConstructionFailedException::class)
fun `construct throws exception when API is less than 24`() {
ReflectionHelpers.setStaticField(Build.VERSION::class.java, "SDK_INT", 23)
val pushTemplate = provideMockedInputBoxPushTemplateWithRequiredData(true)
val result = InputBoxNotificationBuilder.construct(
context,
pushTemplate,
trackerActivityClass,
broadcastReceiverClass
)
assertNull(result)
}

@Test
fun `construct should not have any inputText action if the template is created from intent`() {
val pushTemplate = provideMockedInputBoxPushTemplateWithRequiredData(true)
Expand Down
2 changes: 1 addition & 1 deletion code/testapp/src/main/assets/basic/basic.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"adb_body_ex": "Basic push template with action buttons.",
"adb_a_type": "WEBURL",
"adb_uri": "https://chess.com/games",
"adb_image": "https://i.ibb.co/QN078XB/Resize-image-project.jpg",
"adb_image": "https://images.pexels.com/photos/260024/pexels-photo-260024.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
"adb_act": "[{\"label\":\"Go to chess.com\",\"uri\":\"https:\/\/chess.com\/games\/552\",\"type\":\"DEEPLINK\"},{\"label\":\"Open the app\",\"uri\":\"\",\"type\":\"OPENAPP\"}]",
"adb_sound": "bells",
"adb_channel_id": "2024",
Expand Down
2 changes: 1 addition & 1 deletion code/testapp/src/main/assets/basic/basic_colors.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"adb_body_ex": "Basic push template with action buttons.",
"adb_a_type": "WEBURL",
"adb_uri": "https://chess.com/games",
"adb_image": "https://i.ibb.co/QN078XB/Resize-image-project.jpg",
"adb_image": "https://images.pexels.com/photos/260024/pexels-photo-260024.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2",
"adb_act": "[{\"label\":\"Go to chess.com\",\"uri\":\"https:\/\/chess.com\/games\/552\",\"type\":\"DEEPLINK\"},{\"label\":\"Open the app\",\"uri\":\"\",\"type\":\"OPENAPP\"}]",
"adb_small_icon": "chat_bubble",
"adb_large_icon": "https://cdn-icons-png.flaticon.com/128/864/864639.png",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,21 @@ import android.net.Uri
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.NotificationManagerCompat
import androidx.core.app.RemoteInput
import com.adobe.marketing.mobile.notificationbuilder.PushTemplateConstants
import com.adobe.marketing.mobile.services.Log
import com.adobe.marketing.mobile.services.ServiceProvider

class NotificationTrackerActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
when (intent?.action) {
val intent = intent
when (intent.action) {
"Input Received" -> {
val results = RemoteInput.getResultsFromIntent(intent)
val quickReplyResult = results?.getCharSequence("developer intent action name")
Log.debug("MyApp", "NotificationTrackerActivity", "input box quick reply result: $quickReplyResult")
}
PushTemplateConstants.NotificationAction.CLICKED -> executePushAction(intent)
else -> {}
}
Expand Down