-
Notifications
You must be signed in to change notification settings - Fork 906
Android Tutorial 4
Note: If you haven't completed tutorial 3 yet, we encourage you to do so before jumping into this tutorial.
... type safe parsing ... deep link code in one place ... handle reactivity
The goals of this code lab are to learn the following:
- Understand basics behind RIB workflows
- Learn how to create actionable item interfaces, implement their methods, and create workflows to launch specifics flows via deeplinks.
This codelab will focus on adding a new workflow to launch the app and automatically set the player names and launch a new game.
To save time, the AndroidManifest.xml file for the codelab has been updated to handle any URL with the rib-tutorial
scheme. In addition, the workflow libraries have already been integrated into the tutorial 4 starter code.
...
Before we dive into coding this, I’d like to highlight the Step
class. The Step
type is what all actions must return (so Workflows can chain them together). Each step encapsulates a piece of work to be performed, it also has two generics - the first generic is an optional return type to be passed into the next step (if you don’t have a return value - you can use Step.NoValue), the second generic is the next actionable item interface that the user will be placed in when the step is finished doing its work.
For the RootActionableItem
, we’ll be creating a single action to check if the user is logged in, it will return a step with no return type (because future steps don’t need anything here) and the next actionable item will be the LoggedInActionableItem
.
With that being said, let’s add this new method to the RootActionableItem
:
@Override
public Step<Step.NoValue, LoggedInActionableItem> logIn(final UserName playerOne, final UserName playerTwo) {
return Step.from(
Single.defer(new Callable<SingleSource<? extends Step.Data<Step.NoValue, LoggedInActionableItem>>>() {
@Override
public SingleSource<Step.Data<Step.NoValue, LoggedInActionableItem>> call() {
getRouter().detachLoggedOut();
LoggedInActionableItem loggedInActionableItem
= getRouter().attachLoggedIn(playerOne, playerTwo);
return Single.just(Step.Data.toActionableItem(loggedInActionableItem));
}
}));
}
This tutorial's code would be a lot nicer if you enable lambda's in your project. But still usable without lambdas.
All this does is attach the logged in router, and then return it as the next actionable item. There are a few subtle things worth noting here though:
- All of the code that performs changes is wrapped within the Rx single. If you were to place code outside of this block, it would execute at the time the workflow is created, as opposed to when it is this actions turn to perform its work.
-
attachLoggedIn()
has been updated to return theLoggedInActionableItem
(which is really just the LoggedInInteractor behind an interface). We use the actionable item interface to have a clear list of actions that can be performed instead of just exposing the entire public API for the interactor.
Now that we have a single action defined, let’s create a workflow to test it out. In your root package, create a new class called LaunchGameWorkflow and insert this boilerplate:
public class LaunchGameWorkflow extends RootWorkflow<Step.NoValue, LaunchGameWorkflow.LaunchGameDeepLinkModel> {
public LaunchGameWorkflow(@NonNull Intent deepLinkIntent) {
super(deepLinkIntent);
}
@NonNull
@Override
protected Step<Step.NoValue, ? extends ActionableItem> getSteps(
@NonNull RootActionableItem rootActionableItem,
@NonNull LaunchGameDeepLinkModel launchGameDeepLinkModel) {
return null;
}
@NonNull
@Override
protected LaunchGameDeepLinkModel parseDeepLinkIntent(@NonNull Intent deepLinkIntent) {
return null;
}
@Validated(factory = TrainingSessionsValidationFactory.class)
public static class LaunchGameDeepLinkModel implements RootWorkflowModel {
// Unimplemented class
}
}
This is obviously not fully implemented, but let’s look over a few details here before filling it out. First, let’s take a look at the generics - the first generic is the return type for the entire Workflow, in this case there is no return type. The second generic is the POJO model class that is used to hold information about the deep link.
To create the pojo model, parseDeeplinkIntent will be implemented to convert an intent to a LaunchGameDeepLinkModel. In addition, the deeplink plugin will run this model through RAVE to ensure the data is valid.
Next, the deep link model will be passed to getSteps() to create a workflow with the model and the initial root actionable item.
First, let’s implement the the LaunchGameDeepLinkModel class and parseDeepLinkIntent method to properly pull out paramters into a model.
Your LaunchGameDeepLinkModel should look like the following:
@Validated(factory = TrainingSessionsValidationFactory.class)
static class LaunchGameDeepLinkModel implements RootWorkflowModel {
private final String playerOneName;
private final String playerTwoName;
private final String gameName;
public LaunchGameDeepLinkModel(String playerOneName, String playerTwoName, String gameName) {
this.playerOneName = playerOneName;
this.playerTwoName = playerTwoName;
this.gameName = gameName;
}
@NonNull
public String getPlayerOneName() {
return playerOneName;
}
@NonNull
public String getPlayerTwoName() {
return playerTwoName;
}
@NonNull
public String getGameName() {
return gameName;
}
}
Next we’ll implement parseDeepLink to turn the intent into a LaunchGameDeepLinkModel instance:
@NonNull
@Override
protected LaunchGameDeepLinkModel parseDeepLinkIntent(@NonNull Intent deepLinkIntent) {
Uri uri = deepLinkIntent.getData();
String playerOne = uri.getQueryParameter("playerOne");
String playerTwo = uri.getQueryParameter("playerTwo");
String gameName = uri.getQueryParameter("gameKey");
return new LaunchGameDeepLinkModel(playerOne, playerTwo, gameName);
}
In the event one of these parameters are missing, this model will fail RAVE validation and a workflow won’t be launched.
Next, let’s implement getSteps() to return a single step to log the user in:
@NonNull
@Override
protected Step<Step.NoValue, ? extends ActionableItem> getSteps(
@NonNull RootActionableItem rootActionableItem,
@NonNull LaunchGameDeepLinkModel launchGameDeepLinkModel) {
return rootActionableItem.logIn(
UserName.create(launchGameDeepLinkModel.getPlayerOneName()), UserName.create(launchGameDeepLinkModel.getPlayerTwoName()));
}
Now that we a workflow let’s integrate it.
Copyright © 2017 Uber Technologies Inc.
Once you've read through the documentation, learn the core concepts of RIBs by running through the tutorials and use RIBs more efficiently with the platform-specific tooling we've built.