From Punched Cards to Prompts
AndroidIntroduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
Slices provide the ability for your app to share
information and controls with other apps. In my previous post,
Share a Slice of Your App, I covered
the basics of Slices on Android. Introduced in Android Pie (API 28), they are packaged as a support
library and work down to Android KitKat (API 19). If you haven’t already, give it a read through to
learn about the main concepts in Slices, how to create a simple one, and how to view them on your
devices. Before continuing with this post, make sure you have a SliceProvider in place to build off
of for the interactive Slices.
While basic Slices have their place, you will often want to provide users with more controls.
Over the next few posts I’ll cover the different kinds of actions you can add to Slices and how
they are used.
One way to make a Slice more interactive is to provide multiple actions. These show up as
tappable icons that will take the user to different screens in your app, or take the user to the
same screen with different sets of data. The icons should be descriptive enough that the user knows
which action will occur when they tap on them. In this example, my Slice will prompt users to enter
my app to book a vacation. Each icon will show the mode of transportation they can take to their
vacation: a car, a plane, or a boat.
The first step to adding this type of Slice is to create the new path handling logic in your
onBindSlice()
method. When another app requests this path from my app I will return a Slice
configured with multiple actions the user can take. The path for my multiple action Slice will be
called /multiple
.
override fun onBindSlice(sliceUri: Uri): Slice? {
context ?: return null
return when (sliceUri.path) {
"/multiple" -> {
// Display multiple action slice
}
else -> {
...
}
}
}
Next, I’ll need to create the different SliceAction
objects I want to show in my Slice. Since I
have three different options I want to present, I’ll need three different actions. Each action
needs a PendingIntent
which describes the action to fire when clicked, an icon to display, an
image mode constant that tells the slice how to display the action, and an action title which is
also used for the content description. I’ll use private functions to create each action to keep my
onBindSlice
method from getting too crowded.
override fun onBindSlice(sliceUri: Uri): Slice? {
context ?: return null
return when (sliceUri.path) {
"/multiple" -> {
// Display multiple action slice
val carAction = createCarAction()
val planeAction = createPlaneAction()
val boatAction = createBoatAction()
}
else -> {
...
}
}
}
private fun createCarAction(): SliceAction {
val carText = context.getString(R.string.book_car_text)
val carIntent = Intent(context, BookingActivity::class.java).let {
it.putExtra(EXTRA_BOOKING_STRING, carText)
PendingIntent.getActivity(context, CAR_REQUEST_CODE, it, /*flags:*/0)
}
val carBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.ic_car)
val carIcon = IconCompat.createWithBitmap(carBitmap)
return SliceAction.create(
carIntent,
carIcon,
ICON_IMAGE,
carText
)
}
private fun createPlaneAction(): SliceAction {
val planeText = context.getString(R.string.book_plane_text)
val planeIntent = Intent(context, BookingActivity::class.java).let {
it.putExtra(EXTRA_BOOKING_STRING, planeText)
PendingIntent.getActivity(context, PLANE_REQUEST_CODE, it, /*flags:*/0)
}
val planeBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.ic_plane)
val planeIcon = IconCompat.createWithBitmap(planeBitmap)
return SliceAction.create(
planeIntent,
planeIcon,
ICON_IMAGE,
planeText
)
}
private fun createBoatAction(): SliceAction {
val boatText = context.getString(R.string.book_boat_text)
val boatIntent = Intent(context, BookingActivity::class.java).let {
it.putExtra(EXTRA_BOOKING_STRING, boatText)
PendingIntent.getActivity(context, BOAT_REQUEST_CODE, it, /*flags:*/0)
}
val boatBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.ic_boat)
val boatIcon = IconCompat.createWithBitmap(boatBitmap)
return SliceAction.create(
boatIntent,
boatIcon,
ICON_IMAGE,
boatText
)
}
With the actions in place I can move on to creating my Slice. I’ll set an accent color to tint my
icons, and I’ll use the addAction()
method to add all of my SliceAction
objects.
override fun onBindSlice(sliceUri: Uri): Slice? {
context ?: return null
return when (sliceUri.path) {
"/multiple" -> {
// Display multiple action slice
val carAction = createCarAction()
val planeAction = createPlaneAction()
val boatAction = createBoatAction()
val vacationTitle = context.getString(R.string.vacation_title)
val vacationSubtitle = context.getString(R.string.vacation_subtitle)
list(context, sliceUri, ListBuilder.INFINITY) {
setAccentColor(ContextCompat.getColor(context, R.color.colorPrimary))
header {
title = vacationTitle
subtitle = vacationSubtitle
}
addAction(carAction)
addAction(planeAction)
addAction(boatAction)
}
}
else -> {
...
}
}
}
With the Slice in place, I can create a run configuration to display my Slice in the SliceViewer.
If you don’t know how to run a Slice, check out
my intro post to see how. When I
run my Slice, I’ll see my actions displayed. When I tap on an action, it will take me into my app
based on its PendingIntent
.
Adding multiple actions to your Slices provide a quick way for users to navigate directly to a
screen they’re interested in within your app. These actions can be modified based on how people use
your app so you always provide actions that are most relevant to each user. Getting this right means
users won’t have to go searching through your app for the specific functionality they want. They
can see exactly what they want in your Slice and act on it appropriately.
In the following couple posts we’ll see how to how to add toggle actions and range actions to
your Slices. These kinds of actions are more interesting because you have to create components to
handle data changes when the user interacts with them.
I hope you enjoyed this post. If you have any questions please feel free to comment below. Thanks
for reading!
Introduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
Jetpack Compose is a declarative framework for building native Android UI recommended by Google. To simplify and accelerate UI development, the framework turns the...
Big Nerd Ranch is chock-full of incredibly talented people. Today, we’re starting a series, Tell Our BNR Story, where folks within our industry share...