Skip to main content

Android v26.10.0

Compatibility​

NameVersionImplementation
Compile SDK36compileSdk = q2libs.versions.compileSdk.get()
Target SDK36targetSdk = q2libs.versions.targetSdk.get()
Min SDK29minSdk = q2libs.versions.minSdk.get()
Java / JVM21javaVersion = q2libs.versions.javaVersion
Gradle9.4.1gradle-wrapper.properties
Android Gradle Plugin9.1.0id(q2libs.plugins.android.application)
Kotlin Gradle Plugin2.3.20(built into AGP 9; no explicit plugin alias)
KSP2.3.6id(q2libs.plugins.ksp)
Google Services Plugin4.4.2id(q2libs.plugins.google.services)
Kotlin Serialization2.3.20id(q2libs.plugins.kotlin.serialization)

No toolchain bumps in this release. The table above is unchanged from 26.9.1.

Migration from 26.9.1 to 26.10.0​

Doing a multi-version migration?

If you're jumping more than 2–3 versions at once, walking each version's migration notes individually can be tedious and error-prone. Consider the Large Version Migrations guide instead. It walks you through replacing your local DevApp with a fresh copy at the target tag and dropping your module back in, which atomically picks up every scaffolding change at once.

Nothing in this release breaks compilation. One change alters runtime behavior without a compiler error, so read the supportsBiometrics section below even if your module builds cleanly.

Action required: custom AuthenticationModule must declare supportsBiometrics​

AuthenticationModule gains a supportsBiometrics property, defaulted to false. It gates the biometrics row Q2 publishes into mobile settings. An AuthenticationModule that implements updateBiometrics and isBiometricsEnabled but leaves supportsBiometrics at the default will build fine and silently lose its biometrics settings row.

class MyLoginModule(private val sdkUtils: SdkUtils) : AuthenticationModule {

override val supportsBiometrics = true

override suspend fun updateBiometrics(isOn: Boolean, activity: AppCompatActivity): Boolean { … }
override suspend fun isBiometricsEnabled(activity: AppCompatActivity): BiometricsEnabledState { … }
}

It is not suspend, because the settings list is assembled synchronously for the web app. It answers "is this implemented", not "is biometrics on for this user or available on this device". Return a constant true, not a device capability check.

Previously the row appeared whenever any AuthenticationModule was registered. For a module that did no biometrics, that produced a toggle that flipped itself straight back off.

updateBiometrics and isBiometricsEnabled are now defaulted​

Both members are no longer abstract. A module that does no biometrics implements neither:

suspend fun updateBiometrics(isOn: Boolean, activity: AppCompatActivity): Boolean = false
suspend fun isBiometricsEnabled(activity: AppCompatActivity): BiometricsEnabledState =
BiometricsEnabledState.N_A

Existing overrides keep working unchanged. If you were implementing these only to satisfy the compiler, delete them and leave supportsBiometrics at false.

Planned move

These three members leave AuthenticationModule once a dedicated settings-provider module type exists. They are documented here as the current contract, not as a long-term home.

SdkUtils.loadPathInUuxViewBeforeLogon / AfterLogon are deprecated​

Both are deprecated in favor of openUUXRoute, which replaces the pre- and post-login split with a single call. They still work in 26.10.0, so this migration is optional for this release.

- sdkUtils.loadPathInUuxViewAfterLogon("activity-center")
+ sdkUtils.openUUXRoute("activity-center")

See openUUXRoute below for the parameter shape and the behavior differences.

SdkUtils.isSacViaPushAllowed() is deprecated​

Secure Account Creation capabilities are now exposed as one object. The old method remains as a default implementation delegating to it, so existing call sites keep compiling and working.

- if (sdkUtils.isSacViaPushAllowed()) { … }
+ if (sdkUtils.sacCapabilities.allowSacViaPush) { … }

New SDK interface capabilities​

SdkUtils.openUUXRoute (single-entry UUX navigation)​

/**
* Navigates the UUX web view to an Ember route through the router bridge, so the route is
* (re-)entered and refreshed even when it is already the current route.
*
* If called before the user is authenticated, the route is held and replayed automatically
* once they reach the post-login landing page.
*
* @param route Ember route name, e.g. "activity-center".
* @param queryParams query params passed to the route, e.g. mapOf("isMrdcHistory" to true).
*/
abstract fun openUUXRoute(route: String, queryParams: Map<String, Any> = emptyMap())
sdkUtils.openUUXRoute("activity-center")
sdkUtils.openUUXRoute("activity-center", mapOf("isMrdcHistory" to true))

Three behaviors the deprecated loadPathInUuxView* pair did not have:

  • No pre-/post-login decision. Called while unauthenticated, the route is held in a latest-wins slot and replayed when the user reaches the landing page. Called post-login, it navigates immediately. A DeepLinkModule no longer has to guess which variant to call.
  • Re-enters the current route. Because it goes through the Ember router bridge rather than a path load, calling it for the route already on screen refreshes that route instead of no-opping. This is what makes "open deposit history and show the deposit I just made" work.
  • UUX version differences are handled for you. The bridge picks transitionTo or transitionToRoute, and maps renamed legacy route names, based on the UUX version the app is running against. There is no minimum UUX version to check and no per-FI branching to write.

A held route is dropped on login failure, so a route requested before authentication is never replayed into a different user's session on a shared device.

AuthenticationModule.clearCachedCredentials()​

A new defaulted no-op hook for dropping whatever your module cached at login: a password held for biometric enrollment, a username, a token.

fun clearCachedCredentials() {}

The host calls it on logout, on a profile switch, and best-effort when a live session returns to the login screen. Override this rather than logOut: a profile switch is not a logout, so logOut never fires for one, and a module that clears only there goes on holding one user's password while a second user is signed in.

Contract requirements:

  • Must be idempotent and must not throw. One logout can call it more than once.
  • Arrives on an arbitrary thread. A profile switch or session expiry arrives on the WebView's JavaScript bridge thread, so mark cached fields @Volatile or synchronize.
  • Touch no View and no Compose state. Nothing waits on the call.

It deliberately does not fire part-way through a sign-in, since the module is likely still using the credential. Clear your cache at the start of each login attempt as well and nothing accumulates.

AuthenticationModule.logOut (documented contract)​

No signature change, but the contract is now explicit: the host will not proceed until onComplete is called. Call it on every path including failure, and note that a finally is the safe shape. Otherwise logout never finishes and the user is stranded with no route back to login.

Override logOut only for work that must finish before the login screen returns, which in practice means ending a session held off-device. To clear a local credential cache, use clearCachedCredentials instead: it covers more cases and cannot hang the host.

postQ2LoginResult (hand a completed login back to the host)​

A new Fragment extension in com.q2.sdk_interfaces.authentication replaces hand-rolled setFragmentResult plumbing:

fun Fragment.postQ2LoginResult(response: LoginResponse)
- parentFragmentManager.setFragmentResult(
- AuthenticationModule.ResultKeys.keyFragmentResult,
- Bundle().apply {
- putParcelable(AuthenticationModule.ResultKeys.keyLoginResponse, response)
- }
- )
+ postQ2LoginResult(response)

The host listens on AuthenticationModule.ResultKeys.keyFragmentResult. A wrong key produces no error, just a login that appears to succeed and goes nowhere. That is why this is now a provided extension rather than something each module reproduces.

LoginResponse.OAuthTokenResponse.bioEnablement​

class OAuthTokenResponse(
val q2Token: String,
val idToken: String?,
val accessToken: String?,
val username: String? = null,
val bioEnablement: Boolean = false
) : LoginResponse()

Whether biometric login is enabled for this user on this device. Supplied by the authenticating module, which owns that state, and reported in the logonUser authType audit alongside the authentication method. Defaulted to false, so existing constructor calls are unaffected.

SacCapabilities​

data class SacCapabilities(
val allowSacViaPush: Boolean,
val disableSacModification: Boolean,
)

abstract val sacCapabilities: SacCapabilities

disableSacModification is new in this release and has no deprecated predecessor. It reports that the installation forbids changing an existing Secure Account Creation enrollment, as distinct from allowSacViaPush, which reports whether SAC via push is available at all.


Other changes affecting modules​

SdkUtils.getDeviceID() is backed by a new identifier policy​

getDeviceID() previously read Settings.Secure.ANDROID_ID inline. It now delegates to DeviceIdentifier in the new com.q2.policies library, which returns ANDROID_ID when available and otherwise mints a random UUID once, persists it encrypted, and reuses it for the life of the install. Stored values are read first, so an install that has fallen back can never flip back to ANDROID_ID and change identity mid-life.

For the normal case the returned value is byte-identical to 26.9.1. The change is that getDeviceID() no longer returns an empty or null-backed value on a device where ANDROID_ID is unavailable.

Inbound SSO: deviceIdentifierParamKey​

A new optional key in the Inbound SSO module's settings.json data block. When present, the device identifier is sent to the IDP as a sign-in parameter under that name:

"data": {
"buildProperties": {
"issuer": "…",
"clientId": "…",
"identifier_claim": "sub",
"deviceIdentifierParamKey": "Device-ID"
}
}

Omit the key and no device-identifier parameter is sent. See Configuring Inbound SSO.

Native module launches now sync the navigation drawer​

When the web app opens a UIModule through the module bridge, Core now highlights the matching drawer item for the duration of the module and restores the previous selection when the module is dismissed, including when the module fails to start. Matching is by the route value in the call's data object, falling back to the module identifier.

No module-side change is required. If your module appears in the navigation drawer, confirm the drawer item's route matches the identifier or the route you are launched with, or the highlight will not follow.

ServiceCallsV2.postEmptyLogonUser takes a JsonObject​

- fun postEmptyLogonUser(@Body emptyBody: EmptyLoginUserEntity): Call<ResponseBody>
+ fun postEmptyLogonUser(@Body body: JsonObject): Call<ResponseBody>

EmptyLoginUserEntity is deleted. The capabilities call now sends a real body so the logonUser authType audit fields can ride along. Only relevant if your module called this directly.


Version Updates​

No library, plugin, or toolchain versions changed in this release. The version catalog is identical to 26.9.1.


Version Catalog​

AndroidX Libraries​

LibraryVersionImplementation
AndroidX Core KTX1.12.0implementation(q2libs.androidx.core.ktx)
AndroidX AppCompat1.6.1implementation(q2libs.androidx.appcompat)
AndroidX Activity KTX1.8.1implementation(q2libs.androidx.activity.ktx)
AndroidX Legacy Support1.0.0implementation(q2libs.androidx.legacy.support)
AndroidX Constraint Layout2.1.4implementation(q2libs.androidx.constraintlayout)
AndroidX CardView1.0.0implementation(q2libs.androidx.cardview)
AndroidX Local Broadcast Manager1.1.0implementation(q2libs.androidx.localbroadcastmanager)
AndroidX Percent Layout1.0.0implementation(q2libs.androidx.percentlayout)
AndroidX Biometric1.1.0implementation(q2libs.androidx.biometric)
AndroidX RecyclerView1.3.2implementation(q2libs.androidx.recyclerview)
AndroidX WebKit1.14.0implementation(q2libs.androidx.webkit)
AndroidX Media1.6.0implementation(q2libs.androidx.media)
AndroidX Browser1.8.0implementation(q2libs.androidx.browser)
AndroidX Grid Layout1.0.0implementation(q2libs.androidx.gridlayout)
AndroidX Preference1.2.1implementation(q2libs.androidx.preference)
AndroidX Security Crypto1.1.0-beta01implementation(q2libs.androidx.security.crypto)
AndroidX ExifInterface1.4.2implementation(q2libs.androidx.exifinterface)
AndroidX Credentials (ref only)1.6.0(catalog version pin; no library alias yet)

AndroidX Fragment​

LibraryVersionImplementation
AndroidX Fragment1.7.0implementation(q2libs.androidx.fragment)
AndroidX Fragment KTX1.7.0implementation(q2libs.androidx.fragment.ktx)

AndroidX Navigation​

LibraryVersionImplementation
AndroidX Navigation Fragment KTX2.7.4implementation(q2libs.androidx.navigation.fragment.ktx)
AndroidX Navigation UI KTX2.7.4implementation(q2libs.androidx.navigation.ui.ktx)
AndroidX Navigation Compose2.8.9implementation(q2libs.androidx.navigation.compose)

AndroidX Lifecycle​

LibraryVersionImplementation
AndroidX Lifecycle Runtime KTX2.7.0implementation(q2libs.androidx.lifecycle.runtime.ktx)
AndroidX Lifecycle ViewModel KTX2.7.0implementation(q2libs.androidx.lifecycle.viewmodel.ktx)
AndroidX Lifecycle Process2.7.0implementation(q2libs.androidx.lifecycle.process)
AndroidX Lifecycle Compiler2.7.0ksp(q2libs.androidx.lifecycle.compiler)
AndroidX Lifecycle Extensions2.2.0 (Deprecated)implementation(q2libs.androidx.lifecycle.extensions)
AndroidX Lifecycle ViewModel Compose2.7.0implementation(q2libs.androidx.lifecycle.viewmodel.compose)

AndroidX Room​

LibraryVersionImplementation
Room Runtime2.8.4implementation(q2libs.androidx.room.runtime)
Room Compiler2.8.4ksp(q2libs.androidx.room.compiler)
Room KTX2.8.4implementation(q2libs.androidx.room.ktx)

AndroidX CameraX​

LibraryVersionImplementation
CameraX Camera21.5.0implementation(q2libs.androidx.camera.camera2)
CameraX Lifecycle1.5.0implementation(q2libs.androidx.camera.lifecycle)
Camera View1.5.0implementation(q2libs.androidx.camera.view)

AndroidX Compose​

LibraryVersionImplementation
Compose BOM2025.12.01implementation(platform(q2libs.androidx.compose.bom))
Compose Foundation-implementation(q2libs.androidx.compose.foundation)
Compose UI-implementation(q2libs.androidx.compose.ui)
Compose UI Graphics-implementation(q2libs.androidx.compose.ui.graphics)
Compose UI Tooling-implementation(q2libs.androidx.compose.ui.tooling)
Compose UI Tooling Preview-implementation(q2libs.androidx.compose.ui.tooling.preview)
Compose UI Test Manifest-implementation(q2libs.androidx.compose.ui.test.manifest)
Compose UI Test JUnit4-implementation(q2libs.androidx.compose.ui.test.junit4)
Compose Runtime LiveData-implementation(q2libs.androidx.compose.runtime.livedata)
Material Icons Core-implementation(q2libs.material.icons.core)
Material3-implementation(q2libs.material3)
Material3 Adaptive-implementation(q2libs.androidx.compose.material3.adpative)
Activity Compose1.8.2implementation(q2libs.activity.compose)

Google Libraries​

LibraryVersionImplementation
Material1.10.0implementation(q2libs.google.material)
GSON2.10.1implementation(q2libs.google.gson)
Play Services Vision20.1.3 (Deprecated)implementation(q2libs.google.play.services.vision)
ZXing Core3.5.1implementation(q2libs.google.zxing.core)
Age Signals0.0.4implementation(q2libs.google.age.signals)

Firebase​

LibraryVersionImplementation
Firebase Messaging23.3.0implementation(q2libs.google.firebase.messaging)
Firebase Messaging KTX23.3.0implementation(q2libs.google.firebase.messaging.ktx)
Firebase Instance ID21.1.0implementation(q2libs.google.firebase.iid)
Firebase Core21.1.1implementation(q2libs.google.firebase.core)
Firebase Crashlytics Build Tools2.9.9implementation(q2libs.google.firebase.crashlytics.buildtools)

Kotlin & Jetbrains​

LibraryVersionImplementation
Kotlinx Coroutines1.7.3implementation(q2libs.jetbrains.kotlinx.coroutines)
Kotlinx Serialization JSON1.11.0implementation(q2libs.kotlinx.serialization)
Jetbrains Annotations20.1.0implementation(q2libs.jetbrains.annotations)

Networking​

LibraryVersionImplementation
Retrofit22.9.0implementation(q2libs.retrofit2.retrofit)
Retrofit2 Converter GSON2.9.0implementation(q2libs.retrofit2.converter.gson)
Retrofit2 RxJava Adapter2.1.0implementation(q2libs.retrofit2.adapter.rxjava)
OkHttp34.10.0implementation(q2libs.okhttp3.okhttp)
OkHttp3 Logging Interceptor4.10.0implementation(q2libs.okhttp3.logging.interceptor)
RxAndroid1.2.0implementation(q2libs.reactivex.rxandroid)
Volley1.2.1implementation(q2libs.volley)

Dependency Injection​

LibraryVersionImplementation
Dagger2.59.2implementation(q2libs.dagger)
Dagger Compiler2.59.2ksp(q2libs.dagger.compiler)
Hilt2.59.2implementation(q2libs.hilt)
Hilt Compiler2.59.2ksp(q2libs.hilt.compiler)
Hilt Navigation Compose1.2.0implementation(q2libs.androidx.hilt.navigation.compose)
Glassfish Annotation10.0-b28implementation(q2libs.glassfish.annotation)

Dependency Injection (Koin)​

LibraryVersionImplementation
Koin Android3.4.3implementation(q2libs.koin.android)
Koin Core3.4.3implementation(q2libs.koin.core)
Koin AndroidX Compose3.5.0implementation(q2libs.koin.androidx.compose)

Third-Party Libraries​

LibraryVersionImplementation
EventBus3.3.1implementation(q2libs.eventbus)
EventBus Processor3.3.1ksp(q2libs.eventbus.processor)
Commons IO2.6implementation(q2libs.commons.io)
Picasso2.8implementation(q2libs.picasso)
Coil Compose2.7.0implementation(q2libs.coil)
Coil View2.7.0implementation(q2libs.coil.view)
Timber5.0.1implementation(q2libs.timber)
Apache Commons Imaging1.0-alpha2implementation(q2libs.apache.commons.imaging)

Q2 SDK Components​

LibraryCoordVersionImplementation
Q2 SDK Interfacescom.q2.msdk:sdk_interfacesq2Versionimplementation project(':sdk_interfaces')
Q2 UI Componentscom.q2:ui-components0.1.1implementation(q2libs.q2.ui.components)
Q2 Routing Service(local project, unpublished)q2Versionimplementation project(':modules:q2_routing_service')

Plugin Declarations​

PluginIDImplementation
Android Applicationcom.android.applicationid(q2libs.plugins.android.application)
Android Librarycom.android.libraryid(q2libs.plugins.android.library)
KSPcom.google.devtools.kspid(q2libs.plugins.ksp)
Compose Compilerorg.jetbrains.kotlin.plugin.composeid(q2libs.plugins.compose.compiler)
Hiltcom.google.dagger.hilt.androidid(q2libs.plugins.hilt)
Kotlin Parcelizeorg.jetbrains.kotlin.plugin.parcelizeid(q2libs.plugins.kotlin.parcelize)
Google Servicescom.google.gms.google-servicesid(q2libs.plugins.google.services)
Artifactorycom.jfrog.artifactoryid(q2libs.plugins.artifactory)
Kotlin Serializationorg.jetbrains.kotlin.plugin.serializationid(q2libs.plugins.kotlin.serialization)
Dokkaorg.jetbrains.dokkaid(q2libs.plugins.dokka)

Library Bundles​

BundleLibrariesImplementation
Daggerdagger, glassfish-annotationimplementation(q2libs.bundles.dagger)
Retrofit2retrofit2-retrofit, retrofit2-converter-gsonimplementation(q2libs.bundles.retrofit2)
Koinkoin-android, koin-core, koin-androidx-composeimplementation(q2libs.bundles.koin)