Customizing the Onboarding Flow with SAP BTP SDK for Android 4.0

In the ‘Flow’ component of SAP BTP SDK for Android 4.0, many new features are added to make the component more flexible and easy to use. In this blog post, we’re going to talk about one of them, that is, how to add custom steps into the onboarding process.

The default onboarding process has an ‘End User License Agreement’ (EULA) screen as the first step. It may meet the requirement of some mobile apps, but we often get feedbacks that the client code may need another ‘Privacy Term’ screen before or after EULA.

With the current provided customization options from the ‘Flow’ component, the EULA step can be skipped in the onboarding process, then the client code can implement its own screens for both EULA and the Privacy Term screen before starting the onboarding flow. But this may only works for the single user mode mobile apps. For the multiple user mobile apps, because the create new account flow will be triggered from the ‘Flow’ component and the client code has no controls over it, it’s difficult for the client code to present the same EULA and Privacy Term screens as in the onboarding process.

The new feature in SAP BTP SDK for Android 4.0 is the way to address the above requirement and some other similar requirements.

The above diagram illustrates the onboarding process and the possible custom step insertion points, the client code can exclude some of the optional steps with the existing flow options and service configurations, and insert custom steps at certain points in the onboarding process.

Now let’s dive into the code details to explain how we implement the idea in the above diagram.

sealed class FlowCustomStep<T : FlowStepFragment>( open val stepId: Int, open val stepClass: KClass<T>
) { /** Represents the custom step which will be inserted before the eula step*/ data class BeforeEula<T : FlowStepFragment>( override val stepId: Int, override val stepClass: KClass<T> ) : FlowCustomStep<T>(stepId, stepClass) /** Represents the custom step which will be inserted after the eula step*/ data class BeforeActivation<T : FlowStepFragment>( override val stepId: Int, override val stepClass: KClass<T> ) : FlowCustomStep<T>(stepId, stepClass) data class BeforeAuthentication<T: FlowStepFragment>( override val stepId: Int, override val stepClass: KClass<T> ) : FlowCustomStep<T>(stepId, stepClass) data class BeforePasscodeCreation<T: FlowStepFragment>( override val stepId: Int, override val stepClass: KClass<T> ) : FlowCustomStep<T>(stepId, stepClass) /** Represents the custom step which will be inserted after passcode creation step*/ data class BeforeConsent<T : FlowStepFragment>( override val stepId: Int, override val stepClass: KClass<T> ) : FlowCustomStep<T>(stepId, stepClass) /** Represents the custom step which will be inserted after the consent steps*/ data class BeforeEnd<T : FlowStepFragment>( override val stepId: Int, override val stepClass: KClass<T> ) : FlowCustomStep<T>(stepId, stepClass)
}

With this sealed class, the ‘Flow’ component will know all the necessary information on what steps to be inserted at which points.

Then in ‘FlowActionHandler’, there is one new function added that the client code needs to override to provide custom steps.

open fun getFlowCustomizationStep(runningFlowName: String?) = listOf<FlowCustomStep<*>>()

From the function signature, you can see that the ‘Flow’ component will ask the client code for the custom steps for a given flow identified by the ‘runningFlowName’ argument, and the client code can return a list of single flow steps.

With the current implementation, the ‘Flow’ component will only ask for the custom steps for the ‘onboarding’, ‘create account’ and ‘forgot passcode’ flow, but it’s open for the enhancement on other flows in the future.

Now let’s see how to add a ‘Privacy Term Agreement’ step after EULA in the onboarding flow. Suppose the flow step fragment for this step is already implemented, what the client code needs to do is very simple:

//1. Create a child class of 'FlowActionHandler' and override 'getFlowCustomizationStep'
class MyFlowActionHandler : FlowActionHandler() { override fun getFlowCustomizationStep(runningFlowName: String?): List<FlowCustomStep<*>> { return if (runningFlowName == FlowType.ONBOARDING.name) { listOf( FlowCustomStep.BeforeActivation(R.id.stepPrivacyTerm, PrivacyStepFragment::class) ) } else super.getFlowCustomizationStep(runningFlowName) }
} //2. When starting the onboarding flow, create an instance of the above FlowActionHandler and set it into 'FlowContext' val flowContext = FlowContext( appConfig = AppConfig.Builder().applicationId("app_id").build(), multipleUserMode = true, flowStateListener = MyFlowStateListener(application = application), flowActionHandler = MyFlowActionHandler(), flowOptions = FlowOptions( activationOption = ActivationOption.QR_ONLY ) ) Flow.start(this@SplashActivity, flowContext) { _, resultCode, _ -> if (resultCode == Activity.RESULT_OK) { startMainBusinessActivity() } finish() }

The above sample code will only insert this custom step in the onboarding flow. If you want to insert the same step into the ‘create account’ flow, you can change the ‘if’ part in the sample code.

Please note that the ‘step ID’ for each step will be used by Google Navigation framework, it has to be unique in the navigation graph. We recommend using the step fragment layout resource ID as the step ID.

On how to implement the step fragment, here is the document on how to implement a flow step and create your own flow. The following sample code demonstrates how to move to the next step if ‘Agree’ button is clicked and how to terminate the flow when ‘Deny’.

class PrivacyStepFragment : FlowStepFragment() { private lateinit var binding: PrivacyTermStepBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = PrivacyTermStepBinding.inflate(cloneLayoutInflater(inflater), container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding.agreeButton.setOnClickListener { stepDone(R.id.stepPrivacyTerm) } binding.denyButton.setOnClickListener { //Terminate the current flow FlowInterruptEvent.notifyInterruptEvent( flowViewModel.businessData, FlowInterruptEvent.Error() ) } }
}

When you try to insert custom steps into the onboarding flow, there is one thing you need consider. The onboarding flow will be considered done after the passcode creation step. Steps after that should be considered as optional steps, whose functionalities can be enabled or disabled after the onboarding flow, For example, the usage consent step should have an entrance in the client code to revoke or grant the consent after the onboarding flow. So the ‘Privacy Term Agreement’ step should NOT be inserted after the passcode creation if the onboarding process can only proceed when the user agrees the terms.

We only demonstrate adding one step in the onboarding flow with the above sample code. If you have several custom steps, to share the data among them, you can use the ‘businessDataMap’ inside ‘FlowViewModel’ which is accessible in every step of the flow.

Thanks for reading and looking forward to your feedback. If you have any questions, please leave them here:  https://answers.sap.com/tags/73555000100800001281