Search

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

Brian Gardner

4 min read

Oct 22, 2018

Icing on the Slice: Providing more value to users with range 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
library and work all the way back to Android KitKat (API 19). If you haven’t already, give my introduction to Slices 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.

In my previous post, I covered adding a toggle action to your Slice to make it more
interactive. In this post, I’ll show how to add a range action. Ranges allow users to control any
field that accepts a discrete range of options, such as volume or brightness. This type of control
also requires you to create a component to handle the data in your app. Read on to find out more!

Add range action

Similar to the toggle action, you need to provide a Service or BroadcastReceiver to handle the
data coming from the Slice. For this example, I’ll use a BroadcastReceiver. Don’t forget to
register your receiver in your AndroidManifest.xml so the system can start it properly.

class NerdLevelReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        // Handle the range data from the Intent
    }
}

Again, I’ll fill out the actual implementation once I complete my Slice. This will allow me to
create a valid PendingIntent for my Slice action.

Next, I’ll create a different path for my Slice called /range.

override fun onBindSlice(sliceUri: Uri): Slice? {
    val context = getContext() ?: return null
    return when (sliceUri.path) {
        "/range" -> {
            // Display range slice
        }
        else -> {
            ...
        }
    }
}

One important thing to know about the range action is that you need to specify a primary Slice
action. If you don’t, the app will crash with an IllegalStateException. So first, I’ll
create my primary action for my range Slice:

override fun onBindSlice(sliceUri: Uri): Slice? {
    context ?: return null
    return when (sliceUri.path) {
        "/range" -> {
            // Display range slice
            val primarySliceAction = createPrimarySliceAction()
        }
        else -> {
            ...
        }
    }
}

private fun createPrimarySliceAction(): SliceAction {
    val primaryTitle = context.getString(R.string.view_all_booking_options)
    val primaryIntent = Intent(context, BookingActivity::class.java).let {
        it.putExtra(EXTRA_BOOKING_STRING, primaryTitle)
        PendingIntent.getActivity(context, DEFAULT_REQUEST_CODE, it, 0)
    }
    val primaryBitmap = BitmapFactory.decodeResource(context.resources, R.drawable.ic_main)
    val primaryIcon = IconCompat.createWithBitmap(primaryBitmap)
    return SliceAction.create(
            primaryIntent,
            primaryIcon,
            ICON_IMAGE,
            primaryTitle
    )
}

With that in place, I can move on to creating my Slice. The inputRange is used to create the
range action. A PendingIntent is provided for the component responsible for handling the updates.
A max and current value are also provided to configure the current state of the range.

override fun onBindSlice(sliceUri: Uri): Slice? {
    context ?: return null
    return when (sliceUri.path) {
        "/range" -> {
            // Display range slice
            val primarySliceAction = createPrimarySliceAction()

            val nerdLevelIntent = Intent(context, NerdLevelReceiver::class.java).let {
                PendingIntent.getBroadcast(context, 0, it, 0)
            }

            val nerdLevelTitle = context.getString(R.string.nerd_level_title)
            list(context, sliceUri, ListBuilder.INFINITY) {
                inputRange {
                    title = nerdLevelTitle
                    inputAction = nerdLevelIntent
                    max = 100
                    value = 50
                    primaryAction = primarySliceAction
                }
            }
        }
        else -> {
            ...
        }
    }
}

With my slice implementation in place, I can fill out my BroadcastReceiver. Like the toggle
implementation, the Intent passed to the receiver has extra data about the current value of the
range. This method will be called many times as the user moves the slider so don’t take too long in
your receiver or you risk the system killing your receiver before it finishes.

class NerdLevelReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        if (intent.hasExtra(Slice.EXTRA_RANGE_VALUE)) {
            val rangeValue = intent.getIntExtra(Slice.EXTRA_RANGE_VALUE, -1)
            Log.d("NERD LEVEL", "Have range value: $rangeValue")

            NerdLevelManager.nerdLevel = rangeValue
        }
    }
}

With my receiver in place, I can run my Slice and see the range value output to LogCat as I move
the slider.

Example of a slice displaying a range control

Fin

Range actions provide a great user interface for users to control any functionality that needs a
value from a discrete range. You can either use a Service or a BroadcastReceiver to handle
the user’s input. Just configure a PendingIntent for your component and the system will start it
when the data changes.

I hope you enjoyed these posts on interactive Slices. Watch out for more posts on Slices in the
future. If you have any questions please feel free to comment below. Thanks for reading!

Josh Justice

Reviewer Big Nerd Ranch

Josh Justice has worked as a developer since 2004 across backend, frontend, and native mobile platforms. Josh values creating maintainable systems via testing, refactoring, and evolutionary design, and mentoring others to do the same. He currently serves as the Web Platform Lead at Big Nerd Ranch.

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