Quick Tip: Create a Custom Quick Settings Tile for Android

The quick settings panel needs no introduction to Android users. They use the switches it offers all the time to turn services such as WiFi, Bluetooth, and GPS on and off. Over the years, it has grown and improved so much that there’s rarely a need to open the actual Settings app these days.

Until recently, the quick settings panel was locked, and users had to be satisfied with a large, but fixed collection of switches—commonly known as tiles. With the launch of Android Nougat, though, that’s no longer the case. Users are now free to add, remove, and rearrange tiles. What’s more, as an app developer, you can offer your own custom tiles.

In this quick tip, I’ll show you how to use the Quick Settings Tile API to create a custom tile from scratch.

Prerequisites

Before you proceed, make sure you have the following:

  • the latest version of Android Studio
  • a phone running Android Nougat or higher 

1. Understanding Tiles

In essence, tiles are just readily accessible switches users can press at any time—even when their devices are locked. Because they can display nothing more than an icon and a label, they can only be used for very specific yet simple actions.

Every tile has a state associated with it. Just like a real-world switch, it can either be in an “on” state or an “off” state. As you might expect, a tile that’s on is brighter than one that’s off. Although you must manage the state of your tile yourself, the Android platform automatically manages its brightness.

2. Creating a Custom Tile

In order to offer a custom tile, your app must have a service that extends the TileService class.

class MyTileService: TileService() {

}

While mentioning the service in the project’s manifest, you must give it a label and an icon, both of which will be used to create the default look of the tile. To give you a realistic example, we’ll now be creating a tile that makes the user’s device vibrate continuously as long as it’s on. Its label will be Tremble, and its icon will be vibration, which is available in the Vector Asset Studio under the Notification section.

Vector Asset Studios Notification section

Next, you must add an intent filter to the service’s definition so that it can respond to the android.service.quicksettings.action.QS_TILE action. To make sure that it can be launched only by the Android system, you must also protect it with the android.permission.BIND_QUICK_SETTINGS_TILE permission.

At this point, the service’s definition should look like this:

<service
  android:name=".MyTileService"
  android:enabled="true"
  android:exported="true"
  android:label="Tremble"
  android:icon="@drawable/ic_vibration_black_24dp"
  android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
  <intent-filter>
    <action
      android:name="android.service.quicksettings.action.QS_TILE"/>
  </intent-filter>
</service>

That’s all the code you need to display a custom tile. If you deploy your app now and navigate to the Edit section of the quick settings panel, you should be able to see your tile in the list of available tiles.

Quick settings panels Edit section

Drag it and drop it near one of the default tiles so that you can access it more easily.

Custom tile placed among default tiles

3. Initializing the Tile

You must have noticed that our tile is quite bright. That’s because we still haven’t initialized its state, and the system thinks it is “on”. To change the tile such that it starts in the off state when the user adds it, you can override the onTileAdded() event handler of the service and set the state property of the qsTile object to Tile.STATE_INACTIVE.

Whenever you change the state, you must remember to also call the updateTile() method so that the tile’s looks change to match the state.

override fun onTileAdded() {
    super.onTileAdded()

    // Update state
    qsTile.state = Tile.STATE_INACTIVE

    // Update looks
    qsTile.updateTile()
}

If you run the app now, remove the tile, and add it back again, you should see that it’s off.

Custom tile in off state

4. Adding a Click Handler

Right now, nothing happens when you click on the tile. You can change that by overriding the onClick() event handler of the service.

Inside the event handler, you can turn the tile on and off by alternating between the Tile.STATE_ACTIVE and Tile.STATE_INACTIVE states. The following code shows you how to do so with a simple if-else statement:

override fun onClick() {
    super.onClick()
    if(qsTile.state == Tile.STATE_INACTIVE) {
        // Turn on
        qsTile.state = Tile.STATE_ACTIVE
        startVibrating() // TODO
    } else {
        // Turn off
        qsTile.state = Tile.STATE_INACTIVE
        stopVibrating() // TODO
    }

    // Update looks
    qsTile.updateTile()
}

5. Using the Vibrator

The action associated with the tile should start as soon as the tile is turned on and stop as soon as it’s turned off. Therefore, in addition to updating the state, the code we added in the previous step contains calls to the startVibrating() and stopVibrating() methods.

The startVibrating() method can use the vibrate() method of Android’s Vibrator class to make the phone vibrate. The vibrate() method, however, expects a fixed duration. To make sure that the phone vibrates continuously as long as the tile is on, you can call it inside a loop, preferably with a short duration. Such a loop can’t be run inside the UI thread, the thread the tile service uses for its click event handler.

Ideally, any long-running operation you want your tile service to start or stop should be placed inside an IntentService instance. To keep this tutorial short, though, let’s make do with a coroutine for now.

The following code shows you how to run the loop inside a coroutine created using the launch() coroutine builder:

fun startVibrating() {
    launch {
        while(qsTile.state == Tile.STATE_ACTIVE) {
          (getSystemService(Context.VIBRATOR_SERVICE) as Vibrator)
            .vibrate(1000) // Vibrate for a second

          // Wait for a second before vibrating again 
          delay(1000)
        }
    }
}

Although the above loop will terminate when the user turns the tile off, the vibrations may last for an extra second. To stop them immediately, you can call the cancel() method of the Vibrator service inside the stopVibrating() method.

fun stopVibrating() {
   (getSystemService(Context.VIBRATOR_SERVICE) as Vibrator)
        .cancel()
}

Our custom tile is ready. However, it won’t work unless it has permission to use the phone’s vibrator. You can request it by adding the following line to your project’s manifest file:

<uses-permission android:name="android.permission.VIBRATE"/>

If you deploy the app now and click on the custom tile, your phone should start vibrating. By clicking on it again, you should be able to stop the vibrations immediately.

Custom tile in on state

Conclusion

If you are building an app that offers unique functionality or information users will need access to very frequently, offering a custom tile with it can dramatically improve the user experience. In this tutorial, you saw how easy it is to create such a tile using the Quick Settings Tile API.

To learn more about it, do refer to the official documentation.

And while you’re here, check out some of our other posts on Android app development!

Leave a Reply

Your email address will not be published. Required fields are marked *