Quickstart
This guide sets up your Application class, your screen providers and your MainActivity, then replaces one screen with your own.
There are two ways to build on msa-uikit:
| You want to | Use |
|---|---|
| Change only colours, fonts, logos and sizes | The stock screens with your theme. See Theming & Branding. |
| Redesign some or all screens | A subclass of DefaultScreenProviders that replaces the screens you redesign. Every screen you leave alone keeps the stock design. |
This guide follows the second path, because it includes the first.
Step 1: Define Your Theme
Create one theme value for the whole app. Every place MineSec Pay 7 draws UI reads it: the main activity, the App2App activity and the card-reading screens.
import androidx.compose.ui.graphics.Color
import com.theminesec.multiplatform.msa.theme.MsaBrand
val MyBrandTheme = MsaBrand.fromBrandColor(
brandColor = Color(0xFF0066CC),
gradient = Color(0xFF3388DD),
).toTheme()MsaBrand.fromBrandColor derives the full palette from one colour, and picks white or dark text on it by WCAG
contrast. See Theming & Branding for every value you can set.
Step 2: Screen Providers
ScreenProviders maps every screen to a Composable. The screens are organised in groups (startup, home,
payment, history, settlement, settings, idle), plus the App2App screens.
Subclass DefaultScreenProviders and override only what you redesign. Here the login screen is replaced and every
other screen stays stock:
import androidx.compose.runtime.Composable
import com.theminesec.multiplatform.msa.screens.DefaultScreenProviders
import com.theminesec.multiplatform.msa.screens.startup.DefaultStartupScreens
import com.theminesec.multiplatform.msa.theme.MsaAppTheme
import com.theminesec.multiplatform.msa_core.app.contract.ComposableScreen
import com.theminesec.multiplatform.msa_core.app.contract.screens.StartupScreens
class MyScreenProviders : DefaultScreenProviders(MyBrandTheme) {
// App2App and the card-reading screens run in their own activities and compose
// through this wrapper. MsaAppTheme carries your logos as well as your colours.
override val themeWrapper: @Composable (content: @Composable () -> Unit) -> Unit =
{ content -> MsaAppTheme(MyBrandTheme) { content() } }
override val startup: StartupScreens = object : DefaultStartupScreens() {
override val login = ComposableScreen { state, triggerAction ->
MyLoginScreen(state, triggerAction)
}
}
}
val myScreenProviders = MyScreenProviders()Declare each screen as a stored property (override val x = ...), not a getter (override val x get() = ...). A
getter creates a new screen on every read and forces recomposition.
Step 3: Application Class
Extend BaseMsaApplication. It starts Koin, the PayServer and the POS Connector for you. You supply the environment
and the bindings that only your app can provide:
import com.theminesec.multiplatform.msa_core.app.BaseMsaApplication
import com.theminesec.multiplatform.msa_core.app.config.AppBuildInfo
import com.theminesec.multiplatform.msa_core.app.config.EnvironmentConfig
import com.theminesec.multiplatform.msa_core.app.config.MsaEnvironment
import com.theminesec.multiplatform.msa_core.app.contract.ScreenProviders
import org.koin.core.module.Module
import org.koin.dsl.module
class MyApplication : BaseMsaApplication() {
override fun bootstrapEnvironment() {
EnvironmentConfig.initialize(
EnvironmentConfig(
environment = MsaEnvironment.Staging, // MsaEnvironment.Production for release
licenseFileName = "your-license-file.license",
)
)
}
override fun provideAppKoinModules(): List<Module> = listOf(
module {
single<ScreenProviders> { myScreenProviders }
single<AppBuildInfo> {
object : AppBuildInfo {
override val versionLabel = "${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})"
}
}
}
)
}| Binding | Required | Purpose |
|---|---|---|
ScreenProviders | Yes | The screens. Also read by the App2App and card-reading activities. |
AppBuildInfo | Yes | The version shown in Settings. Only versionLabel must be set. |
Register the class in your AndroidManifest.xml with android:name=".MyApplication". The App2App and card-reading
activities are declared in msa-core's manifest and merge into your app automatically.
Step 4: MainActivity
Your main activity extends BaseMsaActivity, which registers the card reader and loads the per-activity Koin
modules. Apply your theme and pass your providers to App:
import android.os.Bundle
import androidx.activity.compose.setContent
import com.theminesec.multiplatform.msa.theme.MsaAppTheme
import com.theminesec.multiplatform.msa_core.acitivty.BaseMsaActivity
import com.theminesec.multiplatform.msa_core.app.navigation.App
class MainActivity : BaseMsaActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MsaAppTheme(MyBrandTheme) {
App(screenProviders = myScreenProviders)
}
}
}
}| Component | Purpose |
|---|---|
BaseMsaActivity | Registers the card reader and loads per-activity Koin modules. Override provideAdditionalKoinModules() to add your own. |
MsaAppTheme | Applies your theme: colours, type, shapes and logos. |
App() | Entry-point Composable. Handles navigation and passes each screen its state. |
Only re-theming? Keep
MyScreenProvidersfrom Step 2 with just thethemeWrapperoverride, and leave out thestartupoverride. Every screen stays stock and is drawn in your theme, logos included.
Step 5: Implement a Screen (Login Example)
Each screen receives an immutable state and a triggerAction callback. The screen draws the state and reports
what the user did. Navigation and business logic stay in msa-core.
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
import com.theminesec.multiplatform.msa.components.component.button.MsaButton
import com.theminesec.multiplatform.msa.theme.MsaTheme
import com.theminesec.multiplatform.msa_core.feature.login.presentation.LoginAction
import com.theminesec.multiplatform.msa_core.feature.login.presentation.LoginState
@Composable
fun MyLoginScreen(
state: LoginState,
triggerAction: (LoginAction) -> Unit,
) {
var passcode by remember { mutableStateOf("") }
Column(
modifier = Modifier
.fillMaxSize()
.background(MsaTheme.colors.background)
.statusBarsPadding()
.navigationBarsPadding()
.padding(MsaTheme.spacing.md),
verticalArrangement = Arrangement.spacedBy(MsaTheme.spacing.md),
) {
Text(
text = "Passcode Login",
color = MsaTheme.colors.foreground,
style = MsaTheme.typography.headlineSmall,
)
OutlinedTextField(
value = passcode,
onValueChange = { passcode = it.filter(Char::isDigit).take(12) },
modifier = Modifier.fillMaxWidth(),
placeholder = { Text("12-digit passcode") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
singleLine = true,
)
state.errorState?.exception?.errorMessage?.let { message ->
Text(text = message, color = MsaTheme.colors.error, style = MsaTheme.typography.labelMedium)
}
Spacer(Modifier.weight(1f))
if (state.isLoading) {
CircularProgressIndicator(Modifier.align(Alignment.CenterHorizontally))
}
MsaButton(
text = "Login",
enabled = passcode.length == 12 && !state.isLoading,
onClick = { triggerAction(LoginAction.LoginClicked(passcode)) },
modifier = Modifier.fillMaxWidth(),
)
if (state.isBiometricSupported && state.isBiometricEnabled) {
MsaButton(
text = "Use biometrics",
onClick = { triggerAction(LoginAction.BioMetricClicked) },
modifier = Modifier.fillMaxWidth(),
)
}
}
}Key Patterns in This Example
| Pattern | Details |
|---|---|
| State reading | state.isLoading, state.isBiometricSupported, state.isBiometricEnabled, state.errorState |
| Actions | LoginAction.LoginClicked(passCode), LoginAction.BioMetricClicked |
| MsaTheme | Theme tokens: colors, typography, spacing, iconSize, radius, shapes |
| uikit components | Reuse public components such as MsaButton so your screens match the stock ones |
Next Steps
- Theming & Branding: colours, typography, sizes, shapes and logos
- Screen Providers Reference: every screen with its State and Action classes
- Example project (provided to Level 2 customers): the full stock implementation of every screen