Skip to content
Open
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 @@ -3,6 +3,7 @@ package com.onesignal.notifications.internal.badges.impl
import android.content.pm.PackageManager
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.annotation.VisibleForTesting
import com.onesignal.core.internal.application.IApplicationService
import com.onesignal.core.internal.database.IDatabaseProvider
import com.onesignal.core.internal.database.impl.OneSignalDbContract
Expand All @@ -14,11 +15,28 @@ import com.onesignal.notifications.internal.common.NotificationHelper
import com.onesignal.notifications.internal.data.INotificationQueryHelper
import com.onesignal.notifications.internal.limiting.INotificationLimitManager

internal class BadgeCountUpdater(
internal class BadgeCountUpdater private constructor(
private val _applicationService: IApplicationService,
private val _queryHelper: INotificationQueryHelper,
private val _databaseProvider: IDatabaseProvider,
private val _sdkInt: Int,
) : IBadgeCountUpdater {
constructor(
applicationService: IApplicationService,
queryHelper: INotificationQueryHelper,
databaseProvider: IDatabaseProvider,
) : this(applicationService, queryHelper, databaseProvider, Build.VERSION.SDK_INT)

companion object {
@VisibleForTesting
internal fun createForTesting(
applicationService: IApplicationService,
queryHelper: INotificationQueryHelper,
databaseProvider: IDatabaseProvider,
sdkInt: Int,
) = BadgeCountUpdater(applicationService, queryHelper, databaseProvider, sdkInt)
}

// Cache for manifest setting.
private var badgesEnabled = -1

Expand Down Expand Up @@ -50,6 +68,10 @@ internal class BadgeCountUpdater(

override fun update() {
if (!areBadgesEnabled()) return
// On API 26+ the system handles badges via NotificationChannel, and
// ShortcutBadger can cause native SIGSEGV crashes on some OEM devices
// (e.g. Xiaomi Redmi) where the broadcast receiver has buggy native code.
if (_sdkInt >= Build.VERSION_CODES.O) return
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
updateStandard()
} else {
Expand Down Expand Up @@ -83,6 +105,7 @@ internal class BadgeCountUpdater(

override fun updateCount(count: Int) {
if (!areBadgeSettingsEnabled()) return
if (_sdkInt >= Build.VERSION_CODES.O) return
try {
ShortcutBadger.applyCountOrThrow(_applicationService.appContext, count)
} catch (e: ShortcutBadgeException) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package com.onesignal.notifications.internal.badges

import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.os.Build
import com.onesignal.core.internal.application.IApplicationService
import com.onesignal.core.internal.database.IDatabaseProvider
import com.onesignal.notifications.internal.badges.impl.BadgeCountUpdater
import com.onesignal.notifications.internal.badges.impl.shortcutbadger.ShortcutBadger
import com.onesignal.notifications.internal.common.NotificationHelper
import com.onesignal.notifications.internal.data.INotificationQueryHelper
import io.kotest.core.spec.style.FunSpec
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.mockkStatic
import io.mockk.unmockkObject
import io.mockk.unmockkStatic
import io.mockk.verify

private class Mocks {
val applicationService = mockk<IApplicationService>()
val queryHelper = mockk<INotificationQueryHelper>(relaxed = true)
val databaseProvider = mockk<IDatabaseProvider>(relaxed = true)

init {
val context = mockk<Context>()
val packageManager = mockk<PackageManager>()
val applicationInfo = ApplicationInfo()

every { applicationService.appContext } returns context
every { context.packageManager } returns packageManager
every { context.packageName } returns "com.onesignal.example"
every {
packageManager.getApplicationInfo("com.onesignal.example", PackageManager.GET_META_DATA)
} returns applicationInfo
}

fun badgeCountUpdater(sdkInt: Int) =
BadgeCountUpdater.createForTesting(
applicationService,
queryHelper,
databaseProvider,
sdkInt,
)
}

class BadgeCountUpdaterTests : FunSpec({
beforeEach {
mockkObject(NotificationHelper)
every { NotificationHelper.areNotificationsEnabled(any()) } returns true
mockkStatic(ShortcutBadger::class)
every { ShortcutBadger.applyCountOrThrow(any(), any()) } just Runs
}

afterEach {
unmockkStatic(ShortcutBadger::class)
unmockkObject(NotificationHelper)
}

test("update should not use ShortcutBadger on Android O") {
Mocks().badgeCountUpdater(Build.VERSION_CODES.O).update()

verify(exactly = 0) { ShortcutBadger.applyCountOrThrow(any(), any()) }
}

test("updateCount should not use ShortcutBadger on Android O") {
Mocks().badgeCountUpdater(Build.VERSION_CODES.O).updateCount(3)

verify(exactly = 0) { ShortcutBadger.applyCountOrThrow(any(), any()) }
}

test("updateCount should use ShortcutBadger before Android O") {
val mocks = Mocks()
val updater =
BadgeCountUpdater(
mocks.applicationService,
mocks.queryHelper,
mocks.databaseProvider,
)

updater.updateCount(3)

verify(exactly = 1) { ShortcutBadger.applyCountOrThrow(any(), 3) }
}
})
Loading