Push notifications for the Android apps of Salla stores, from Kotlin and Java.
Quick start · API reference · Troubleshooting · Log codes · Example app · Changelog
SallaPush is for the mobile apps of Salla stores. Contact the Salla team for your app id.
- Version: 1.1.0. See CHANGELOG.md.
- Also for: iOS · Flutter · React Native · Capacitor
Contents: Requirements · Installation · Platform setup · Quick start · Usage · Other push SDKs · Example app · Troubleshooting · Support · Contributing · License
| Minimum | |
|---|---|
| Android | 6.0 (API 23) |
| compileSdk | 34 |
| Language | Kotlin 1.9, or Java |
| Push | Firebase Cloud Messaging, on devices with Google Play services |
SallaPush brings com.google.firebase:firebase-messaging:24.1.0, androidx.core:core:1.13.1,
androidx.work:work-runtime:2.9.1 and org.jetbrains.kotlin:kotlin-stdlib:1.9.24. A newer version in your app wins.
SallaPush is on Maven Central. In settings.gradle.kts, declare Google's repository and Maven Central, as new Android
projects already do:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}Then add the dependency to your app module's build.gradle.kts:
dependencies {
implementation("com.salla:mobile-push:1.1.0")
}- Groovy: the repository block reads the same in
settings.gradle, and the dependency isimplementation 'com.salla:mobile-push:1.1.0'in the app module'sbuild.gradle. - Version catalog: declare
sallapush = { module = "com.salla:mobile-push", version = "1.1.0" }under[libraries]ingradle/libs.versions.toml, and writeimplementation(libs.sallapush). - Projects with repositories of their own, declared in a build file or added by a plugin as in React Native apps:
add
mavenCentral()there too. - Updating: change the exact version you pin, and read its entry in CHANGELOG.md.
SallaPush's notifications arrive through Firebase Cloud Messaging. Add Firebase to your app as
Firebase's guide shows: your app's google-services.json in the app
module, and the Google services Gradle plugin. Leave Firebase's auto-init on (the default).
Inside <application> in your AndroidManifest.xml:
<meta-data
android:name="com.salla.push.default_notification_icon"
android:resource="@drawable/ic_stat_notification" />The icon must be white on transparent: Android draws only its shape. Without it, SallaPush uses Firebase's
com.google.firebase.messaging.default_notification_icon when your app sets one, and otherwise shows a bell and logs
display.default_icon. Channels, colour and sound has the optional
keys.
Android gives each FCM message to one FirebaseMessagingService. If your app has none, SallaPush's own service gets
them: skip this step. If your app or another library in it declares one, declare your own service and forward to
SallaPush first, as the sample does:
package com.salla.push.sample
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.salla.push.SallaPush
class SampleMessagingService : FirebaseMessagingService() {
override fun onMessageReceived(message: RemoteMessage) {
// First line: SallaPush shows its own messages and returns true; everything else is false.
if (SallaPush.Notifications.handleFirebaseMessage(applicationContext, message)) return
// Not a SallaPush message: hand it to your other SDKs here.
}
override fun onNewToken(token: String) {
SallaPush.Notifications.onNewToken(applicationContext, token)
// Give the token to your other SDKs here.
}
}<service
android:name=".SampleMessagingService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>When a library declares a messaging service too, give your <intent-filter> android:priority="1", so that FCM
delivers to yours (Your FCM service).
Start SallaPush in your Application class's onCreate, with your app id, and name that class in the manifest's
android:name. The sample's Application starts with:
SallaPush.initWithContext(this, "00000000-0000-4000-8000-000000000002")
// Added here, the click listener also receives the tap that started the app.
SallaPush.Notifications.addClickListener { event -> SampleRouter.open(this, event) }initWithContext returns at once; calls made before it are kept until it runs. SampleRouter stands for your app's
navigation (Notification taps and deep links).
On Android 13 and later the user must allow notifications. SallaPush never asks on its own: ask where it fits your app, as the sample does from a button:
SallaPush.Notifications.requestPermission(fallbackToSettings = true) { granted ->
showPermissionAnswer(granted)
}The callback runs once, on the main thread; from coroutines, call requestPermissionSuspend(fallbackToSettings). Java
makes the same calls through getters such as SallaPush.getNotifications() (Java).
Verify it works. In a debug build, set SallaPush.Debug.logLevel = SallaPushLogLevel.VERBOSE before
initWithContext, or the com.salla.push.LOG_LEVEL meta-data to verbose. Run the app on a device, or an emulator
image with Google APIs, allow notifications, and filter Logcat with adb logcat -s SallaPush: expect init.started,
then token.received and register.ok at the first launch, and login.ok once a customer is logged in;
SallaPush.Notifications.permission is true once notifications are allowed.
// After the customer signs in:
SallaPush.login(customerId, identityToken)
// After the customer signs out:
SallaPush.logout()Call login at each launch too, while the customer is signed in (details).
SallaPush.Notifications.permission says whether notifications are allowed, canRequestPermission() whether the
prompt can still show, and addPermissionObserver reports changes (Permission).
The quick start's click listener gets every tap, on the main thread, with the notification in event.notification.
Route it with the typed getters:
private fun open(notification: SallaPushNotification) {
when (notification.getString("type")) {
"product" -> notification.getLong("id")?.let { router.showProduct(it) }
"order" -> notification.getString("id")?.let { router.showOrder(it) }
else -> notification.launchUrl?.let { router.openUrl(it) }
}
}- Start screens from the
ApplicationwithIntent.FLAG_ACTIVITY_NEW_TASK(Opening a screen from the listener). notification.launchUrlholds the notification's URL. WithSallaPushConfig.Builder().setOpenLaunchUrls(true), a tap opens an https URL in the browser or the app that handles it; your listener still gets the tap, so don't open the URL again.
Pass a lambda to every listener and observer, except SallaPushUserObserver: in Kotlin, implement it with
object : SallaPushUserObserver { … }. SallaPush keeps each one until you remove it, so remove those of a screen with
it, as the sample's screen does in onDestroy:
SallaPush.User.removeObserver(userObserver)
SallaPush.User.pushSubscription.removeObserver(subscriptionObserver)
SallaPush.Notifications.removePermissionObserver(permissionObserver)
SallaPush.Notifications.removeForegroundLifecycleListener(foregroundListener)
SallaPush.Debug.removeLogListener(logListener)While the app is in the foreground, foreground listeners run before SallaPush shows a notification and can hold it back:
SallaPush.Notifications.addForegroundLifecycleListener { event ->
if (checkout.isInProgress) {
event.preventDefault()
checkout.whenFinished { event.notification.display() }
}
}Return quickly: the listeners have at most 500 ms. display() shows the notification once, within 10 minutes.
SallaPush.User.addTag("add_to_cart", System.currentTimeMillis().toString())
SallaPush.User.addTags(mapOf("total_cart_amount" to total.toString(), "cart_items" to items.toString()))- Keys are lowercased: use
a-z,0-9and_, up to 64 characters. Values are cut at 255 characters, and a device holds at most 100 tags.removeTagandremoveTagsremove them;getTags()returns them. - A logout clears the tags;
SallaPushConfig.Builder().setClearTagsOnLogout(false)keeps them. Set a customer's tags after theirlogin. - Pass your app's language with
SallaPush.User.setLanguage(languageCode), such asar, whenever it changes.
SallaPush.User.pushSubscription holds the device id, the push token and the opt-in state, and its observer reports
their changes. optOut() stops SallaPush notifications on the device; optIn() turns them back on, and shows the
permission prompt while it can still show.
If your app needs the user's consent first, require it at every launch, before initWithContext:
SallaPush.setConsentRequired(true)
SallaPush.initWithContext(this, "00000000-0000-4000-8000-000000000002")Then call SallaPush.setConsentGiven(true) when the user agrees: until then, SallaPush sends nothing. false
withdraws consent: SallaPush opts the device out, deletes its tags and unlinks the customer
(Consent).
SallaPush logs to Logcat under the tag SallaPush, at WARN by default. To send errors to your own telemetry:
val telemetryListener = SallaPushLogListener { event ->
telemetry.report("sallapush", event.code, event.message, event.metadata)
}
SallaPush.Debug.addLogListener(telemetryListener, SallaPushLogLevel.ERROR)Every entry has a stable code: match on it, not on the message (Log codes).
Android launchers count an app's notifications in the shade for the icon's badge, as a dot or a number.
autoClearBadge, off by default: the badge follows SallaPush's notifications in the shade. Turned on, SallaPush removes its notifications from the shade when the app comes to the foreground. (The iOS SDK clears by default.)autoUpdateBadge, on by default: SallaPush's notification channels show badges.setAutoUpdateBadge(false)creates them without, so its notifications add nothing to the badge.
To clear the badge when the app opens:
val config = SallaPushConfig.Builder()
.setAutoClearBadge(true) // remove SallaPush's notifications from the shade when the app opens
.build()
SallaPush.initWithContext(this, "00000000-0000-4000-8000-000000000002", config)To clear it at another moment, call clearAllNotifications() (Badges).
SallaPush takes over nothing in your app: it shows and reports only its own notifications, and
handleFirebaseMessage returns false for every other message, so your other push SDKs keep working. In a
FirebaseMessagingService that serves several SDKs, call SallaPush first, as in
step 3.
sample builds without any secret: an Application that starts SallaPush as the quick start does and logs the
signed-in customer in at each launch, the forwarding service, and a screen to sign in, ask for the permission, opt out
and in, set a tag, and follow SallaPush's log lines and taps. Open the repository in Android Studio and run sample. To
receive notifications, put your app id in SampleApp.kt and your Firebase values in ~/.gradle/gradle.properties, as
CONTRIBUTING.md shows.
| Problem or log code | Fix |
|---|---|
Gradle: Could not find com.salla:mobile-push |
Add mavenCentral() to your repositories (Installation), and check the version. |
token.firebase_missing or token.missing |
Add Firebase (step 1), and forward onNewToken (step 3). |
fcm.forwarding_hint, or no notification shows |
Forward FCM messages to SallaPush first (step 3). |
register.dormant |
Check your app id. |
display.default_icon |
Add a white-on-transparent small icon (step 2). |
| The badge stays when the app opens | Expected by default on Android. To remove SallaPush's notifications when the app opens, setAutoClearBadge(true) (Badges). |
docs/TROUBLESHOOTING.md has more. When you ask for help, include the SDK version, the Android version and device, and the SallaPush log codes at verbose level.
- SDK bugs and feature requests: open an issue with the SDK and Android versions and the SallaPush log codes you saw. Never paste tokens, keys or customer data.
- App ids: Contact the Salla team for your app id.
Pull requests are welcome. Read CONTRIBUTING.md first. Everyone taking part follows the Code of Conduct.
The SallaPush Android SDK is available under the MIT License.