Pre-built Jetpack Compose views for unlocking, credential handling, error states, and feedback — ready to drop into your Android app and fully customizable to match your brand.
- 🚀 Fast integration — Add complete access flows in minutes with
SeamAccessView
. - 🎨 Customizable — Material3 theming via
SeamTheme
(colors, fonts, key cards, unlock cards). - ✅ Production-ready — Includes error handling, retries, haptic feedback, and animations out of the box.
- 🔗 Seam SDK integration — Works seamlessly with Seam Mobile SDK.
- 🧩 Composable — Use all-in-one views or mix with your own UI components.
- 📱 Modern Android — Built with Jetpack Compose and Material3 design system.
To integrate the Seam Phone Android SDK into your application, please ensure your project meets the following minimum requirements:
-
Compile SDK: 34
-
Kotlin Version: 2.1.0 or greater
-
Minimum Android SDK: The required
minSdk
depends on the specific Seam integration modules you include:- Base SDK (Core, API, Common, Network, Analytics): API Level 24 (Android 7.0)
- Including
saltoks
orsaltospace
: API Level 24 (Android 7.0) - Including
latch
: API Level 26 (Android 8.0) - Including
assaabloy
: API Level 28 (Android 9.0)
Your application's
minSdk
must be set to the highest level required by any of the Seam modules you use.
This project relies on the Seam Mobile SDK artifacts hosted on GitHub Packages. To successfully build the project, you need appropriate credentials.
The settings.gradle.kts
file configures Gradle to look for dependencies in the Seam GitHub Packages repository:
// getPropertyOrNull is a helper function defined in settings.gradle.kts
// to safely read properties from local.properties
fun getPropertyOrNull(propertyName: String): String? {
val propertiesFile = file("local.properties")
if (!propertiesFile.exists()) return null
val properties = Properties()
properties.load(propertiesFile.inputStream())
return properties.getProperty(propertyName, null)
}
// settings.gradle.kts
repositories {
// ... other repositories
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/seampkg/seam-mobile-sdk")
credentials {
username = getPropertyOrNull("seamUsername")
password = getPropertyOrNull("seamPat")
}
}
}
if you are using groovy, your settings.gradle
should look like this:
// getPropertyOrNull is a helper function defined in settings.gradle
// to safely read properties from local.properties
String getPropertyOrNull(String propertyName) {
def propertiesFile = file("local.properties")
if (!propertiesFile.exists()) return null
def properties = new Properties()
properties.load(propertiesFile.inputStream())
return properties.getProperty(propertyName, null)
}
// settings.gradle
repositories {
// ... other repositories
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/seampkg/seam-mobile-sdk")
credentials {
username = getPropertyOrNull("seamUsername")
password = getPropertyOrNull("seamPat")
}
}
}
Gradle requires a username and a Personal Access Token (PAT) with read:packages
scope to access this repository. These credentials must be defined in a local.properties
file at the root of the project:
# local.properties (DO NOT COMMIT THIS FILE)
seamUsername=YOUR_GITHUB_USERNAME
seamPat=YOUR_SEAM_PROVIDED_PAT
Replace YOUR_GITHUB_USERNAME
with your GitHub username. For the Personal Access Token (PAT), please ask Seam for a token with the necessary read:packages
scope.
Important: The local.properties
file is included in .gitignore
and should never be committed to your version control system.
The specific Seam SDK components are included in the app/build.gradle.kts
.
The seam-phone-sdk-android-core
contains the core code for the SDK. It is necessary for all other components to work.
The other dependencies are related to specific integrations. For example, if you want to use the Salto Space integration, you need to add seam-phone-sdk-android-saltospace
to the project.
You can add more integrations dependencies as needed.
Kotlin script (app/build.gradle.kts
)
val seamVersion = "3.0.12" // Or the desired version
dependencies {
// ... other dependencies
implementation("co.seam:seam-phone-sdk-android-seamcomponents:$seamVersion")
implementation("co.seam:seam-phone-sdk-android-core:$seamVersion")
// include as needed
implementation("co.seam:seam-phone-sdk-android-saltoks:$seamVersion")
implementation("co.seam:seam-phone-sdk-android-saltospace:$seamVersion")
implementation("co.seam:seam-phone-sdk-android-latch:$seamVersion")
implementation("co.seam:seam-phone-sdk-android-assaabloy:$seamVersion")
// ...
}
Groovy script (app/build.gradle
)
// app/build.gradle.kts
val seamVersion = "3.0.12" // Or the desired version
dependencies {
// ... other dependencies
implementation "co.seam:seam-phone-sdk-android-seamcomponents:$seamVersion"
implementation "co.seam:seam-phone-sdk-android-core:$seamVersion"
implementation "co.seam:seam-phone-sdk-android-saltoks:$seamVersion"
implementation "co.seam:seam-phone-sdk-android-saltospace:$seamVersion"
implementation "co.seam:seam-phone-sdk-android-latch:$seamVersion"
implementation "co.seam:seam-phone-sdk-android-assaabloy:$seamVersion"
// ...
}
Before running the app, you need a Client Session Token (CST). This token authenticates the mobile device with Seam's backend. You typically obtain this token through your backend server after authenticating the user. Pleas ask Seam on how to obtain a CST since it is not part of the mobile SDK.
The fastest way to get started is with SeamAccessView
, which orchestrates all underlying components to deliver a complete unlock experience:
import co.seam.seamcomponents.SeamAccessView
@Composable
fun MyAccessScreen() {
SeamAccessView(
clientSessionToken = "seam_cst_your_token_here"
)
}
That's it — you now have a fully functional unlock UI in your Android app.
SeamAccessView
automatically integrates with the Seam Mobile SDK for:
- SDK initialization — Automatic setup with your CST
- Device discovery — Bluetooth/NFC scanning and connection
- Credential management — Automatic sync and caching
- Unlock flows — Provider-specific unlock sequences
- Error handling — Network, permission, and hardware error recovery
The Seam Components handle SDK initialization, activation, and state management transparently.
SeamAccessView
— Complete access management interfaceSeamUnlockCardView
— Individual credential unlock interfaceSeamCredentialsView
— Credential list and management
UnlockContent
— Unlock progress and controlsUnlockHeader
— Credential information displayKeyCardComponent
— Individual credential cardsErrorBanner
— Contextual error messagingSeamButton
— Branded action buttons
import co.seam.seamcomponents.SeamAccessView
@Composable
fun MyAccessScreen() {
// Let's initialize the Seam SDK with the client session token
val context = LocalContext.current
LaunchedEffect(showSeamComponent) {
SeamSDK.initialize(
context,
"seam_cst_your_token_here"
)
// Let's activate the Seam SDK after initializing it
SeamSDK.getInstance().activate()
}
var unlockKeyCard by remember { mutableStateOf<KeyCard?>(null) }
// Rendering the Cards view
SeamCredentialsView(
onNavigateToUnlock = { keyCard ->
// handling card click.
unlockKeyCard = keyCard
}
)
// Navigate to Unlock view when unlockKeyCard is not null
unlockKeyCard?.let {
SeamUnlockCardView(
keyCard = it,
onNavigateBack = {
unlockKeyCard = null
}
)
}
}
Seam Mobile Components are fully brandable using Material3 theming via SeamComponentsTheme
. Customize colors, typography, and component styles.
private val DarkColorScheme =
darkColorScheme(
primary = Color(0xFFADC7FF),
onPrimary = Color(0xFF002E69),
...
)
private val LightColorScheme =
darkColorScheme(
primary = Color(0xFFAD00FF),
onPrimary = Color(0xFFBA2E69),
...
)
// Create your Theme as usual
@Composable
fun MyTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
// Instead of MaterialTheme use SeamComponentsTheme here
SeamComponentsTheme(
colorScheme = colorScheme,
content = content,
)
}
Typography is optioal, but you can Material Typography as usual
private val DarkColorScheme =
darkColorScheme(
primary = Color(0xFFADC7FF),
onPrimary = Color(0xFF002E69),
...
)
private val LightColorScheme =
darkColorScheme(
primary = Color(0xFFAD00FF),
onPrimary = Color(0xFFBA2E69),
...
)
val Typography = Typography(
bodyLarge = TextStyle(
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
),
titleLarge = TextStyle(
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
),
...
)
// Create your Theme as usual
@Composable
fun MyTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
// Instead of MaterialTheme use SeamComponentsTheme here
SeamComponentsTheme(
colorScheme = colorScheme,
typography = Typography,
content = content,
)
}
It is possible to go further with customization by setting the seamTheme
attribute on SeamComponentsTheme
.
@Composable
fun MyTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
// Define the Key Card Style
val keyCardStyle = SeamKeyCardStyle(
backgroundGradient = listOf(Color.Red, Color.Cyan),
textColor = Color.Green,
accentColor = Color.Green,
cornerRadius = 24.dp,
shadowColor = Color.Gray,
shadowYOffset = 15.dp,
errorColor = Color.Cyan
)
val seamComponentsThemeData = SeamComponentsThemeData(
keyCard = keyCardStyle
)
// Instead of MaterialTheme use SeamComponentsTheme here
SeamComponentsTheme(
seamTheme = seamComponentsThemeData,
colorScheme = colorScheme,
typography = Typography,
content = content,
)
}
It is similar to the Key Card customization
@Composable
fun MyTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
// Define the Key Card Style
val keyCardStyle = SeamKeyCardStyle(
backgroundGradient = listOf(Color.Red, Color.Cyan),
textColor = Color.Green,
accentColor = Color.Green,
cornerRadius = 24.dp,
shadowColor = Color.Gray,
shadowYOffset = 15.dp,
errorColor = Color.Cyan
)
// Define Unlock Card Style
val unlockCardStyle = SeamUnlockCardStyle(
keyButtonGradient = listOf(Color.Red, Color.Cyan),
keyButtonShadowColor = Color.Red,
keyButtonShadowRadius = 12.dp,
bulletBackground = Color.Magenta,
bulletTextColor = Color.Cyan,
instructionTextColor = Color.Green,
headerBackground = Color.Cyan,
headerTitleColor = Color.Green,
headerSubtitleColor = Color.Magenta,
keyIconColorIdle = Color.Green,
keyIconColorActive = Color.Cyan,
successColor = Color.Magenta,
successIconColor = Color.Green,
errorColor = Color.Cyan,
)
val seamComponentsThemeData = SeamComponentsThemeData(
keyCard = keyCardStyle,
unlockCard = unlockCardStyle
)
// Instead of MaterialTheme use SeamComponentsTheme here
SeamComponentsTheme(
seamTheme = seamComponentsThemeData,
colorScheme = colorScheme,
typography = Typography,
content = content,
)
}
Issues and pull requests are welcome! Please open a GitHub issue for bugs, feature requests, or integration questions.
For questions about Seam Mobile Components:
- 📧 Email: [email protected]
- 📖 Documentation: docs.seam.co