06 December

Custom Quick Tiles Settings in Android 7

In recent editions of Android system, settings underwent a small revolution. The main settings screen has been reorganized, new options have been added. Android Lollipop introduced shortcuts to quickly switch most important settings so that they are visible immediately after expanding the upper panel. Android Nougat, along with the API 24, provided us with the ability to add our own settings there related to our application.

When to use

We shouldn’t abuse this ability. It shouldn’t normally be used for starting our application – we have the desktop for that. The motivation for creating your own tile should be the desire to provide a fast ability to change a setting or turning the application on in a particular mode. A necessary condition is that the associated action was urgent enough for the user, and at least in theory used often. This should not be a change of some secondary option. It also should not be an action that a standard, active user of our application will use 2-3 times a year. Then it really does not make any sense.

A good practice might be to show a dialogue asking if the user definitely wants to perform a given action, particularly if it is irreversible or long-term.

Examples of good use
  • Remotely turning on an external device or changing mode (switching off the light in an intelligent house, monitoring, turning on a cleaning robot).
  • Enabling / disabling our application running in the background, data synchronization.
  • Opening application on a specific, important application screen, preferably with additional options.
How to do it

The entire control of the tile is implemented in a dedicated to it service by expanding TileService class. This class is available from API 24, and our application is probably going to support lower systems. There are no obstacles for it. Then, this option will simply not be available to the user. Marking our service with an appropriate annotation will do.

@TargetApi(Build.VERSION_CODES.N)

As with every service we also need to define it in the manifest. Particular attention should be paid to the name of permit and the action which we listen.

       
           
               
           
       

In the class implementation itself, we will be able to override the following methods:

  • onTileAdded() - Called when user adds the tile to his own panel (it should be noted that the application can add the tile to the set of available tiles, while the user has to edit the upper panel himself and drag it to the actively used tiles).
  • onStartListening() – called when the tile becomes visible, for example after expanding the upper panel.
  • onClick() – called when user clicks the tile.
  • onStopListening() – called when the tile stops being visible on the screen.
  • onTileRemoved()– called when the tile is removed from the tiles currently used in panel.

Attaching to the above methods will give us full control over what is happening and will make it possible to respond accordingly, and possibly alter the view state of the tile itself. It can be unchangeable when we are dealing with a single action, but mostly should reflect the active or inactive state of the selected option. You can also set the state to unavailable, so the tile will be greyed out and non-clickable. It is useful when a given device does not support our functionality and does not have a Bluetooth module in the appropriate version, or we do not have an Internet connection. Exemplary implementation of method that reacts to clicking and changes tile state:

/**
* Called when the user taps the tile.
*/
@Override
public void onClick() {
   Log.d("QS", "Tile tapped");
   //do your stuff here
   updateTile();
}


// Changes the appearance of the tile.
private void updateTile() {
   Tile tile = this.getQsTile();
   boolean isActive = getServiceStatus();


   Icon newIcon = Icon.createWithResource(getApplicationContext(),
          isActive ? android.R.drawable.ic_lock_power_off : ic_android_black_24dp);
   StringBuilder stringBuilder = new StringBuilder();
   stringBuilder
           .append(getString(R.string.tile_label))
           .append(" ")
           .append(getString(isActive ? R.string.service_inactive : R.string.service_active));
   String newLabel = stringBuilder.toString();
   int newState = isActive ? Tile.STATE_INACTIVE : Tile.STATE_ACTIVE;


   // Change the UI of the tile.
   tile.setLabel(newLabel);
   tile.setIcon(newIcon);
   tile.setState(newState);


   // Need to call updateTile for the tile to pick up changes.
   tile.updateTile();
}
Safety

It is very important to remember about the possibility of expanding the upper panel by the user when screen is locked. Then, we cannot share any sensitive options or actions. Otherwise, securing phone with pin number, pattern to draw or even a fingerprint could be in vain. If we assume that the given option should not be able to run on the lock-screen, there is a simple solution. TileService class that we expand gives us a method.

void unlockAndRun(Runnable runnable);

Thanks to it we can call a request for unlocking phone and then immediately call our action. Whether the phone is locked or not, the isLocked() method will tell us. If we want to call the action on locked screen and we do not really need the authorization, we have to remember, that it will be impossible to show the dialogue. Instead, we can run our own activity which set the flag: FLAG_SHOW_WHEN_LOCKED to getWindow() in the activity. The best method for running it is startActivityAndCollapse, to automatically hide the upper panel so the user would notice that anything happened at all.

Summary

I encourage you to make use of the ability to add your own tiles. Let’s do it, but moderately, not by force. The panel should have only the most urgent and the most commonly used shortcuts, so let’s not turn it into a trash heap of icons. I guess no one would want that.