Native Android SDK for notification-driven accommodation discovery. When your app captures a destination, dates, or a specific hotel, the SDK schedules a single, well-timed local notification that opens a curated Stay22 booking experience. The Android contract mirrors the iOS SDK public surface where platform differences allow it.
- Kotlin · minSdk 24 (Android 7.0)
- No GPS permission required
- You control enablement, notification permission, and user consent
The SDK is published as a Maven artifact (com.stay22:sdk) to a Maven repository
hosted in this repository.
Add the Stay22 Maven repository alongside Google and Maven Central (e.g. in
settings.gradle.kts):
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://raw.githubusercontent.com/Stay22/stay22-android-sdk/main/maven") }
}
}Then add the dependency in the app module's build.gradle.kts:
dependencies {
implementation("com.stay22:sdk:1.6.4")
}- Ensure
minSdkis 24 or newer in the app module'sdefaultConfig. - The published POM declares the SDK's transitive dependencies, so they resolve
automatically as long as
google()andmavenCentral()are present. - The
.aarmerges its ownAndroidManifest.xml, which contributes theINTERNETandPOST_NOTIFICATIONSpermissions, abookingscheme<queries>entry, and an internal notification-tap activity. Do not redeclare these in the host manifest.
If you prefer not to use the Maven repository, download stay22-sdk.aar, place it in
the app module's libs/ directory (e.g. app/libs/), and depend on it directly. A
raw .aar carries no POM, so declare the transitive dependencies explicitly:
dependencies {
implementation(files("libs/stay22-sdk.aar"))
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.work:work-runtime-ktx:2.9.0")
implementation("androidx.lifecycle:lifecycle-process:2.7.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
}Initialize once from Application.onCreate(). Android requires the Application
argument so the SDK can register lifecycle callbacks before activities start.
import android.app.Application
import com.stay22.sdk.Stay22
import com.stay22.sdk.TravelContext
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
Stay22.setEnabled(this, userHasOptedInToStay22Offers)
Stay22.initialize(application = this, aid = "your-partner-id")
}
}
Stay22.requestNotificationPermission()
Stay22.setTravelContext(
TravelContext(
address = "Paris, France",
checkinDate = "2026-03-20",
checkoutDate = "2026-03-25"
)
)Register the Application subclass in the manifest with android:name=".MyApp" if it
is not already registered. Calling initialize more than once is safe; later calls
are ignored.
Recommended practices when enabling Stay22:
- consider surfacing opt-in language before enabling Stay22;
- do not require Stay22 or notification permission for core app functionality;
- provide an in-app opt-out control.
Stay22.setEnabled(application, userHasOptedInToStay22Offers)If your app requires an affirmative consent choice, add this inside the host app's
<application> element. The SDK then stays disabled until you pass an affirmative
choice to setEnabled:
<meta-data
android:name="com.stay22.sdk.RequiresConsent"
android:value="true" />Call setEnabled(application, choice) before or after initialization. It saves the
choice across launches. Call it with false when consent is withdrawn to clear
pending Stay22 notifications and prevent new ones. To read the enabled state before
initialization, use Stay22.readEnabled(application).
Stay22.requestNotificationPermission { canShowNotifications ->
android.util.Log.d("Stay22", "Notifications available: $canShowNotifications")
}requestNotificationPermission(callback) reports true through the callback if
Stay22 can already show notifications. Otherwise, on Android 13 (API 33) and newer,
it requests permission from the current resumed Activity. If permission is unavailable
and the SDK is uninitialized or no Activity is resumed, the callback reports false
without showing a prompt. On Android 12 and older, it reports the current state
without prompting. Use hasNotificationPermission() when you only need the current
state. The no-argument requestNotificationPermission() returns the permission state
before the prompt is answered.
Pass explicit travel context when your app knows the user's destination intent.
import com.stay22.sdk.TravelContext
Stay22.setTravelContext(
TravelContext(
address = "Paris, France",
latitude = 48.8566,
longitude = 2.3522,
checkinDate = "2026-03-20",
checkoutDate = "2026-03-25",
hotelName = "Hotel Example"
)
)TravelContext fields match iOS:
| Field | Description |
|---|---|
address |
City, venue, hotel, or formatted destination. |
latitude / longitude |
Optional destination coordinates. |
checkinDate / checkoutDate |
Optional dates in YYYY-MM-DD format. |
hotelName |
Optional specific hotel name. |
Notes:
setTravelContextmerges non-empty fields, so destination and dates can arrive on different screens.- Scheduling only uses explicit travel intent: address, coordinates, or hotel name.
- The IP/home city is used for suppression only; it is never used as a destination.
To clear explicit context:
Stay22.clearTravelContext()
// or
Stay22.advanced.clearTravelContext()import com.stay22.sdk.NotificationConfig
import com.stay22.sdk.NotificationInterruptionLevel
Stay22.notificationConfig = NotificationConfig(
title = "Hotels in {destination}",
subtitle = "{checkin} - {checkout}",
message = "Tap to find the best deals nearby",
categoryId = "stay22_destinations",
threadId = "stay22",
interruptionLevel = NotificationInterruptionLevel.active
)Supported placeholders are {destination}, {checkin}, and {checkout}, which the
SDK fills from the current travel context at schedule time ({checkin}/{checkout}
resolve to empty when no dates are set). Unsupported tokens are left unchanged.
NotificationConfig fields match iOS:
| Field | Default |
|---|---|
title |
"Hotels in {destination}" |
subtitle |
null |
message |
"Tap to find the best deals nearby" |
categoryId |
"stay22_destinations" |
threadId |
"stay22" |
badge |
null |
launchImageName |
null |
targetContentIdentifier |
null |
interruptionLevel |
null |
relevanceScore |
null |
attachments |
emptyList() |
actions |
emptyList() |
Android-specific notification channel and small-icon selection are internal. The app icon is used as the notification icon when available. For a reliable Android notification small icon, provide a monochrome drawable in the host app and point the SDK at it from the app manifest:
<application>
<meta-data
android:name="com.stay22.sdk.notification_small_icon"
android:resource="@drawable/ic_stat_notification" />
</application>Use a simple white/transparent status-bar style drawable rather than the adaptive launcher icon.
Stay22.advanced.setEventHandler { event ->
when (event) {
is Stay22Event.NotificationScheduled -> Unit
is Stay22Event.NotificationClicked -> Unit
else -> Unit
}
}
Stay22.advanced.scheduleNotification()scheduleNotification() runs the same scheduling pipeline as automatic background
scheduling, using the current travel context and server-configured delay. The SDK
keeps at most one pending Stay22 notification; a changed travel context replaces it,
while the same context keeps the existing timer.
Stay22.advanced.setEventListener { event ->
when (event) {
is Stay22Event.LocationUpdated -> Unit
is Stay22Event.NotificationScheduled -> Unit
is Stay22Event.NotificationShown -> Unit
is Stay22Event.NotificationClicked -> Unit
is Stay22Event.NotificationBlocked -> Unit
is Stay22Event.NotificationSkipped -> Unit
is Stay22Event.NotificationCancelled -> Unit
is Stay22Event.EnabledChanged -> Unit
Stay22Event.TravelContextCleared -> Unit
}
}For local and staging validation only:
Stay22.testing.force = true
Stay22.testing.showLogs = trueForce mode uses a 5-second notification delay and skips local gates so the same flow can be tested repeatedly. Do not ship production builds with force mode on.
| Condition | Behavior |
|---|---|
| SDK disabled | Pending notification state is cleared and no new notification is scheduled. |
| Missing destination | Scheduling is skipped. |
| App foregrounded | Automatic scheduling waits for background; refreshed context can update an existing pending notification, and advanced scheduling can run foreground. |
| Home destination | Scheduling is skipped when the destination matches the server-detected home city. |
| Partner config disabled | Pending notification state is cleared. |
| Duplicate context | Existing pending notification timer is kept. |
| Notification permission denied | Scheduling/showing is blocked and an event is emitted. |
| Local cooldown | Scheduling is skipped. |
The SDK keeps at most one pending Stay22 notification at a time. Server partner config and Nova decisions control remote enablement, delay, distance, cooldown, and frequency.
The SDK does not request device GPS permission and is not used for cross-app tracking. It may send the partner ID, explicit destination context, and anonymous SDK interaction events to Stay22. The host app is responsible for consent, privacy policy disclosures, and app-store privacy answers.
| API | Purpose |
|---|---|
Stay22.initialize(application, aid) |
Start the SDK from Application.onCreate(). Android requires the Application for lifecycle callbacks. |
Stay22.setEnabled(application, enabled) / readEnabled(application) |
Save or read consent before initialization. |
Stay22.isEnabled |
Enable/disable scheduling after initialization. |
Stay22.isInitialized |
Whether initialization completed. |
Stay22.notificationConfig |
Notification text and notification metadata. |
Stay22.campaignId |
Optional attribution value added to generated URLs. |
Stay22.hasNotificationPermission() |
Current Android notification permission state. |
Stay22.requestNotificationPermission(callback) |
Request Android 13+ notification permission and learn whether Stay22 can show notifications. |
Stay22.setTravelContext(...) |
Provide destination context. |
Stay22.clearTravelContext() |
Clear explicit travel context and pending notification state. |
Stay22.advanced.setEventListener(...) |
Register an event listener. |
Stay22.advanced.setEventHandler(...) |
Register a closure-style event handler. |
Stay22.advanced.clearTravelContext() |
Advanced namespace equivalent of clearing travel context. |
Stay22.advanced.scheduleNotification() |
Run the normal scheduling pipeline for the current context. |
Stay22.testing.force |
Testing-only force flag. |
Stay22.testing.showLogs |
Testing-only SDK logging flag. |
This SDK is proprietary and closed source. © 2026 Stay22 Technologies Inc. All Rights Reserved. Use is governed by the LICENSE and any separate written agreement with Stay22. Contact your Stay22 representative for terms.
Questions? See https://dev.stay22.com/docs/mobile-sdk or contact your Stay22 representative.