A clean status bar with Android System UI and QuickDemo

More often than necessary, screenshots and videos of Android apps suffer from showing an untidy status bar with unwanted notification icons, a drained battery indicator, and a different time on every screenshot. In this blog post, we take a look at the Android System UI demo mode that remedies the aforementioned problems and present a tool QuickDemo for fast and convenient status bar cleansing.

A special Thank You to everyone who joined this month’s Android Heads Meetup at Google Vienna. It was a real pleasure talking to you. You can find my talk’s slides here. For all of you that couldn’t listen to my presentation, check out this blog post! 😉

At PSPDFKit, we’re developing PDF Viewer – a PDF app for Android and iOS that has a clean look, is simple to use, and enormously powerful! We put a lot of effort into making the app, as well as the PDF framework powering it, look as clean as possible. However, our quality control does not end with the app, but extends to screenshots and videos taken of the app, as well. A high quality screenshot speaks for itself and shows the level of detail that goes into every aspect of our products.

In the past we had two primary ways of capturing clean screenshots and videos on Android:

  1. Clean status bar is an app for developers. It draws on top of the original dirty status bar, hiding it below a clean-looking overdraw. While this works well when showing opaque status bars with a fixed background color, it cannot be used for transparent status bars or background colors that dynamically change based on the displayed activity’s theme (like the real status bar does). The lack of color adaption is immediately noticeable in videos that show transitions between differently styled activities or when opening modal dialogs that darken the activity and status bar in the background.

    ![Overdrawn status bar](/images/blog/2016/clean-statusbar-with-quickdemo/overdrawn-statusbar.opt-c22b3adb.gif)
    Previously: Opaque drawing on top of the original status bar.
  2. Since the launch of Android 6.0 Marshmallow, we’ve been using the System UI demo mode – an operating system setting that populates the systems status bar with fake data, to give it a cleaner look. However, since the standard System UI demo mode did not deliver all the features we required, we went ahead and started customizing this component.

Today, we’re open sourcing QuickDemo for Android – a small tool that helps developers capture clean screenshots and videos of their apps without a cluttered status bar. If you’re interested in how QuickDemo works, make sure to read the rest of this blog post. 🙌

Android System UI

With Android 6.0 Marshmallow, Google introduced a new customizable system component, the System UI Tuner. Once enabled, the System UI Tuner allows you to change appearance of the status bar, notifications, and other system UI components.

One more feature that was added with the System UI Tuner is the so-called “Demo Mode.” When enabling the demo mode, Android provides fake data to the status bar, giving it a clean and uncompromised look. This allows for shooting clean screenshots and videos (for blog posts, presentation slides, and your app’s Google Play Store page). It’s also great for doing live demos, without suffering from a dirty notification tray, empty battery indicators, or a clock showing a time that indicates yet another late night coding marathon.

However, there are two downsides of the “vanilla” Android demo mode:

  1. Inflexible: You can’t decide on what you would like to show in the status bar. The system settings don’t provide any way to customize the demo mode. Luckily, there is a way to do this via system broadcast intents, which we’re going to show further down in this article.
  2. Inconvenient: It is complicated to enable and disable demo mode, as the switch to enable it is buried deep inside the developer options. That’s where quick settings become interesting, since they provide a way to quickly access actions from any screen on your phone.

The next section will look at how to control the demo mode. The final section will round up the QuickDemo app by implementing a quick settings tile.

Controlling the demo mode

There’s an internal documentation page of the System UI demo mode, which shows how the System UI can be set to demo mode and how demo mode can be configured. All that is required is sending broadcast messages to the System UI, carrying the desired commands.

This is an example of how you could access the demo mode from the command line via adb.

1
2
# Sets the clock to 07:00
adb shell am broadcast -a com.android.systemui.demo -e command clock -e hhmm 0700

Your app can do the same, with the right permissions. Since demo mode is a system component and not part of the Android framework, your app requires the permission android.permission.DUMP to control it.

Copy
AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
<!--  This snippet omits some manifest entries and attributes for brevity. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.pspdfkit.tools.demotile">

    <uses-permission
        android:name="android.permission.DUMP"
        tools:ignore="ProtectedPermissions" />

    ...
</manifest>

Moreover, since this is a protected permission, you can’t simply request that permission at runtime like you do with normal permissions, but need to use adb to manually grant that permission.

1
adb shell pm grant com.pspdfkit.tools.demotile android.permission.DUMP

Note that this must be done every time you uninstall the app or clear its data. After permissions have been acquired, you can use a broadcast intent to control the demo mode. The equivalent code for the command to set the time looks like this:

Copy
1
2
3
4
val intent = Intent("com.android.systemui.demo")
intent.putExtra("command", "clock")
intent.putExtra("hhmm", "0700")
context.sendBroadcast(intent)

Have a look at the DemoMode class in QuickDemo, which implements controls for most of the existing demo mode properties. In the next section, we’re implementing a custom quick settings tile, to control the demo mode’s visibility with just a single tap.

Adding a DemoModeTileService

Ever since Android launched, the operating system features the status bar and the notification drawer. Both vanilla Android and OEM specific OS versions also provided “quick settings,” small buttons at the top of the notification drawer that would give access to important functions like Wi-Fi, flight mode, the phone’s torch, etc.

With 5.0 Lollipop and Material design’s triumphal entry into Android, Google reworked the quick settings section, making it simpler to use and friendlier on the eye. One major version later, 6.0 Marshmallow refined the quick settings, giving the user the possibility to change his/her order, appearance, or completely remove unwanted items.

Fast forward to 2016 and Android 7.0 Nougat, the Android API 24 added support for creating custom quick setting tiles. The new TileService provides access to a single quick settings Tile. An app can register as many tile services (thus tiles) as it wants by declaring them inside its AndroidManifest.xml file.

Copy
AndroidManifest.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!--  This snippet omits some manifest entries and attributes for brevity. -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.pspdfkit.tools.demotile">

    <application>
        <service
            android:name=".service.DemoModeTileService"
            android:icon="@drawable/ic_demo_mode"
            android:label="@string/demo_mode_tile_title"
            android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
            <intent-filter>
                <action android:name="android.service.quicksettings.action.QS_TILE" />
            </intent-filter>
        </service>
    </application>
</manifest>

The registered DemoModeTileService declares the required permissions for using the tiles API, as well as an <intent-filter> that will allow the service to register itself inside the “tiles picker” so it can be discovered by the user. Moreover, it specifies the tiles default icon and title. While both values can be changed dynamically at runtime, the operating system will use the manifest’s values when displaying the tile inside the tiles picker (think of it as a preview).

Now for implementation. Don’t be scared of it – it’s really simple. The DemoModeTileService extends Android framework’s TileService class. To allow toggling of the demo mode by tapping it, the tile service overrides three methods:

  • #onCreate is called when the service is created. Our service creates its custom DemoMode for controlling visibility of the demo mode.
  • #onStartListening is called when the service should start updating the tile, normally before the tile becomes visible. When this happens the DemoModeTileService refreshes the tile’s title, icon, and state.
  • #onClick is called whenever the user tapped the tile in the quick settings. This is where we’re toggling the demo mode between being enabled or disabled. Since the tile service requires a special permission to control the System UI demo mode, we’re also checking for that permission.
Copy
TileService.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class DemoModeTileService : TileService() {

    private lateinit var demoMode: DemoMode

    override fun onCreate() {
        super.onCreate()
        demoMode = DemoMode.get(this)
    }

    override fun onStartListening() {
        qsTile.icon = Icon.createWithResource(this, R.drawable.ic_demo_mode)
        qsTile.label = "Demo mode"
        updateIcon()
    }

    override fun onClick() {
        if (!demoMode.requiredPermissionsGranted) {
            startActivityAndCollapse(SetupGuideActivity.intent(this))
            return
        }

        demoMode.enabled = !demoMode.enabled
        updateIcon()

        val it = Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
        sendBroadcast(it)
    }

    private fun updateIcon() {
        qsTile.state = if (demoMode.enabled) Tile.STATE_ACTIVE else Tile.STATE_INACTIVE
        qsTile.updateTile()
    }
}

That’s it! You can check out the full source code of QuickDemo on GitHub. If you have any questions on the System UI Demo Mode or the Android tiles API, feel free to ping me on Twitter or Google+.

PSPDFKit for Android

Download the free 60-day trial and add it to your app today.