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, and no sdk_interfaces signature changed. One change
can affect release builds only, so read the keep-rule section below even if your module builds and
runs cleanly in debug.
Action required: verify your module in a minified release build
The consumer ProGuard/R8 rules that sdk_interfaces and Core ship to your app are now much
narrower. Earlier versions kept far more than the host needed, and some modules came to depend on
that by accident. A module that did will still pass every debug test and then fail in a release
build with ClassNotFoundException, NoSuchMethodException, or a Gson model whose fields come back
null.
Module classes. The -keep class * implements <module interface> { public *; } rules for
SecurityModule, LifecycleModule, AuthenticationModule, UIModule, PushReceiverModule,
NotificationModule, and DeepLinkModule are gone. MethodModule and CameraModule implementers
still keep public *, because the host looks up their methods by name. For every other module type,
one rule now keeps only what the host calls by reflection:
-keep class * implements com.q2.sdk_interfaces.SDKModule { public <init>(com.q2.sdk_interfaces.SdkUtils); public static *** getInstance(com.q2.sdk_interfaces.SdkUtils); }
Methods that implement a Q2 interface are still kept, because they override a kept interface
method. Any other public member on your module class is no longer kept on your behalf. The
zero-argument <init>() and getInstance() forms are no longer kept either. The host never called
them, since it always passes SdkUtils.
App-wide rules removed from Core. These matched classes in every module, including yours:
Removed rule
Depend on it if your module…
-keep class **.R and -keep class **.R$* { <fields>; }
reads R fields by reflection
-keepclassmembers class * { static final String *; }
reads constants by reflection or by field name
-keep class **.web.** { *; }
has a package named web that Gson or reflection uses
-keep public class * extends android.support.v4.** {*;}
still extends a support-library class
If any of these apply, add the narrowest rule that covers your own classes to your module's
consumer-rules.pro. Don't copy the old blanket rules back. Then build a minified release variant
and exercise the module end to end before you submit it. See
Consumer ProGuard Rules.
AuthenticationModule is now a documented module type
AuthenticationModule lets a module replace Q2's login screen with its own. The host shows your
fragment on launch, after logout, and on UUX timeout. Your fragment hands the login result back,
and Q2 takes over the session from there.
The interface and the com.q2.morannon.auth facade it pairs with shipped in 26.10.0. They are
unchanged in 26.11.0 and are now documented as a supported module type:
Q2Login authenticates against Q2. It covers password login, biometric login and enrollment,
remembered users, maintenance status, and the pre-login notice. No member throws. Failures come
back as values.
Fragment.completeQ2Login(result) hands the result to the host.
Q2SessionCredentials is a thread-safe holder for the password that biometric enrollment needs.
Clear it from clearCachedCredentials().
Only one authentication module may be enabled at a time. The class must declare
AuthenticationModule directly, not inherit it from a base class, or the host falls back to Q2's
login screen without an error. See
Authentication Module Interface for the
full contract, registration rules, biometrics flow, and limitations.
The members added to AuthenticationModule in 26.10.0 (supportsBiometrics,
clearCachedCredentials, postQ2LoginResult, and the documented logOut contract) are covered on
the same page. See the
26.10.0 release notes for when each was introduced.
MethodModule callback payloads are now JSON-escaped
Core now serializes the callModule response, and every error payload, with Gson before passing it
to the web app. Before this, a returnValue string or an exception message that contained an
apostrophe or a quote broke out of the JavaScript literal the bridge builds. The web app then
received nothing.
No module-side change is required. The web app sees the same decoded values as before.
callModule now also rejects a methodName that is not a valid Java identifier with
missingRequiredParameters, rather than attempting the lookup. A real method name always passes.
When content opened in the over-browser (presentUrl(url, WebNavigationTarget.OVER_BROWSER))
embeds a PDF in an <iframe>, the over-browser now renders it with a bundled PDF viewer. It no
longer leaves a blank frame or closes on the download. No module-side change is required.