Search

Icing on the Slice: Providing more value to users with multiple actions

Brian Gardner

4 min read

Sep 10, 2018

Icing on the Slice: Providing more value to users with multiple actions

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.

Add multiple actions

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.

Example of a slice displaying three vacation actions as tappable icons

Fin

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!

Juan Pablo Claude

Reviewer Big Nerd Ranch

During his tenure at BNR, Juan Pablo has taught bootcamps on macOS development, iOS development, Python, and Django. He has also participated in consulting projects in those areas. Juan Pablo is currently a Director of Technology focusing mainly on managing engineers and his interests include Machine Learning and Data Science.

Speak with a Nerd

Schedule a call today! Our team of Nerds are ready to help

Let's Talk

Related Posts

We are ready to discuss your needs.

Not applicable? Click here to schedule a call.

Stay in Touch WITH Big Nerd Ranch News