Skip to content

Overview

The Flows library provides a default implementation for your app's onboarding and restoring scenarios. These scenarios are based on the existing Foundation and Fiori libraries.

These libraries define their own callback interfaces which should be implemented by the app developer. By using the flows you will get not only the interface, but also a default implementation which links the foundation functionality with the UI activities.

At the same time all the customization options of the UI are still supported, and additional custom code can be added to flows as well. The flows executes the onboarding and restoring scenarios as a linear step sequence. Custom code can be added through custom steps added by the app developer.

Flow Types

The Flows library currently supports the following scenarios (flow types) for onboarding flows implementation:

  • Onboard
  • Restore
  • Reset
  • Change

The sequence of the steps is determined by the app developer. The proposed default step sequences are listed in this section.

The Onboard Scenario

Step Description
PasscodePolicyStoreStep Responsible for opening the passcode policy store.
WelcomeScreenStep Responsible for getting configuration data.
OTPConfigurationStep Optional step before the authentication step.
BasicAuthStep, SAML or OAuth Responsible for authentication (and registration in case of BasicAuthStep).
OTPAuthValidationStep Optional step after authentication if the OtpConfigurationStep was used.
SettingsDownloadStep Responsible for getting the policy data.
LoggingStep Responsible for the initialization of the Foundation logger.
StoreManagerStep Responsible for the initialization of the application store on the device.
BasicAuthStoreStep or OAuthStoreStep Responsible for persisting the authentication data into the application store.
WelcomeScreenStoreStep Responsible for persisting the configuration data into the secure store.
SettingsStoreStep Responsible for persisting the policy data into the secure store.
EULAScreenStep Responsible for the approval of the EULA and storing its version number in the secure store.

The Restore Scenario

Step Description
PasscodePolicyStoreStep Responsible for opening the passcode policy store.
StoreManagerStep Responsible for the initialization of the application store on the device.
WelcomeScreenStoreStep Responsible for restoring the configuration data from the application store.
SettingsStoreStep Responsible for restoring the policy data from the application store.
LoggingStep Responsible for the initialization of the Foundation logger.
OTPConfigurationStep Optional step before authentication.
BasicAuthStep, SAML or OAuth Responsible for authentication.
OTPAuthValidationStep Optional step after authentication if the OtpConfigurationStep was used.
BasicAuthStoreStep or OAuthStoreStep Responsible for restoring the authentication data from the application store.
SettingsDownloadStep Responsible for getting the server side policy data.
ChangePasscodeStep Responsible for changing the passcode if the server side passcode policy was changed.
EULAScreenStep Responsible for the approval of the EULA and storing its version number in the secure store.

The Reset Scenario

Step Description
StoreManagerStep Responsible for resetting the application store on the device.
BasicAuthStep, SAML or OAuth Responsible for resetting the authentication data.
PasscodePolicyStoreStep Responsible for resetting the passcode policy store.

The Change Scenario

Step Description
PasscodePolicyStoreStep Responsible for opening the passcode policy store (optional).
ChangePasscodeStep Responsible for changing the passcode (in order to use the already existing passcode policy store, the passcode policy store should be set on the initial flowContext).

Starting the Flow

First, the caller activity, which is the root activity of the app or a dedicated logon activity, should bind to the service responsible for executing the flows. This can be implemented via the standard bindService method of the caller activity:

callerActivity.bindService(new Intent(this, FlowManagerService.class),
                callerActivity.connection, Activity.BIND_AUTO_CREATE);

The FlowManagerService.class parameter value addresses the service and the connection parameter value should implement the ServiceConnection callback interface. Please note that the bindService call is asynchronous. Therefore, you should implement your own business logic in the caller service in order to wait for the return of the callback methods.

The following is a sample implementation of the callback as inner class:

ServiceConnection connection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
        // This is called when the connection with the service has been
        // established, giving us the service object we can use to
        // interact with the service.  Because we have bound to a explicit
        // service that we know is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        flowManagerService = ((FlowManagerService.LocalBinder)service).getService();
    }

    public void onServiceDisconnected(ComponentName className) {
        // This is called when the connection with the service has been
        // unexpectedly disconnected -- that is, its process crashed.
        // Because it is running in our same process, we should never
        // see this happen.
        flowManagerService = null;
    }
};

Once the flowManagerService member variable was successfully initialized (the service was bound), then you can execute the flow.

OnboardingContext flowContext = new OnboardingContext();

EULAScreenSettings eulaScreenSettings;
String eulaVersion;
PasscodePolicyStoreStep passcodePolicyStoreStep = new PasscodePolicyStoreStep();
WelcomeScreenStep welcomeScreenStep = new WelcomeScreenStep();
BasicAuthStep basicAuthStep = new BasicAuthStep();
BasicAuthStoreStep basicAuthStoreStep = new BasicAuthStoreStep();
SettingsDownloadStep settingsDownloadStep = new SettingsDownloadStep();
StoreManagerStep storeManagerStep = new StoreManagerStep();
EulaScreenStep eulaScreenStep = new EulaScreenStep();
WelcomeScreenStoreStep welcomeScreenStoreStep = new WelcomeScreenStoreStep();
SettingsStoreStep settingsStoreStep = new SettingsStoreStep();

// Creating flow and configuring steps
Flow flow = new Flow("onboard");
flow.setSteps(new Step[] {
        passcodePolicyStoreStep,
        welcomeScreenStep,
        basicAuthStep,
        settingsDownloadStep,
        storeManagerStep,
        basicAuthStoreStep,
        welcomeScreenStoreStep,
        settingsStoreStep,
        eulaScreenStep
});

// Preparing flow context
flowContext.setFlowPresentationActionHandler(new
        FlowPresentationActionHandlerImpl(this));
welcomeScreenStep.setApplicationId("com.sap.test.odata");
flowContext.setContext(this);
eulaScreenSettings = new EULAScreenSettings();
eulaVersion = "0.1";
eulaScreenStep.setEulaScreenSettings(eulaScreenSettings);
eulaScreenStep.setEulaVersion(eulaVersion);

Please note that the constructor of the Flows object specifies the type of the flow. In this case, an "onboard" flow was created. Please note also that this example is simplified not following the MVP pattern.

The last step to start the flow is to call the execute method of the service:

flowManagerService.execute(flow, flowContext, new FlowActionHandler() {
                @Override
                public void onFailure(Throwable t) {
                  //error handling
                }

                @Override
                public void onSuccess(FlowContext result) {
                  //saving the onboarded status
                  //start the app
                }
            });

The flow is executed in a background thread; that is, not on the main thread. On the other hand, the flow is executed as a foreground service, not as a background service. As the flow starts, the root (or logon) activity should be the foreground activity and the flow opens new activities on top of this caller activity, but the app will be in foreground until the end of the onboarding process.

If the app goes into the background, then the flow has to be restarted from the beginning. In case of onboarding flow, the state of the flow is not persisted for security reasons because this would require persisting cipher and passcode information as well.

Custom Step Implementation

You can implement a custom step for the flow as following:

  1. Implement the Step or the AsyncResultStep interface of the flows library.
  2. Create a method with the following signature:
public void methodName(FlowContext flowContext, StepActionHandler stepActionHandler)
  1. Annotate this method with StepName where the parameter of the annotation is the name of the scenario where the given method has to be executed. Multiple StepName annotations are supported on a single method.
@StepName("onboard")
  1. (Optional) AsyncResultStep implementations have to implement the getResultActionHandler method of the interface.

Last update: August 12, 2020