Debug Drawers and You: Adding A Debug Drawer To Your App

Debug Drawers and You: Adding A Debug Drawer To Your App

Said another way, how to supercharge your development workflow with a debug drawer.

As Android developers, we're constantly looking for ways to streamline our workflow.

One often-overlooked tool that can significantly boost your development process is the debug drawer.

In this post, we'll explore how to implement and leverage debug drawers to take your Android app development to the next level.

What is a Debug Drawer?

A debug drawer is a hidden panel in your app that contains various tools and information useful during development and testing.

Think of it as a Swiss Army knife for developers – always there when you need it, but invisible to regular users.

dd

Adding A Debug Drawer To Your App

The concept we are going to employ to add a debug drawer to your app is to use debug and release configurations in Gradle to include different modules depending on the build.

To add a debug drawer to your app, we will first start by creating a new module.

In this example, I'll call it :debug-drawer

We are also going to want to create a no-op release implementation of this module so we can have different behaviors in debug vs. release builds.

In this example, I'll call it :debug-drawer-release

Lastly, we are going to create a :debug-drawer-api module for common configurations of our debug drawer feature.

In our :app module, I'm going to add :debug-drawer to the :app module as a dependency via the debugImplementation Gradle configuration.

I'm also going to add the :debug-drawer-release to the :app module as a dependency via the releaseImplementation Gradle configuration.

Lastly, I'm going to add :debug-drawer-api to the :app module as a dependency via the regular implementation Gradle configuration

Now comes the actual implementation of opening the debug drawer!

In this example, we will be opening the debug drawer by pressing the volume down key two times.

The main reason for this is most developers on our team use emulators, where sliding to open can be awkward.

Add the following code to the :debug-drawer-api module:

interface DebugOnlyKeyListener {
  fun onKeyDown(event: KeyEvent)
}

Then, we will modify the :debug-drawer and :debug-drawer-release modules to both depend on the :debug-drawer-api module via the implementation Gradle configuration.

Now, we can move on to actually creating debug and release implementations of DebugOnlyKeyListener

We will create classes with the same name that implement the DebugOnlyKeyListener.

The one that lives under :debug-drawer-release will have a no-op implementation, while the one that lives under :debug-drawer will have an actual implementation.

Now, let's create the actual implementation for DebugOnlyKeyListener in the :debug-drawer module!

import android.app.Application
import android.view.KeyEvent
import com.example.debugdrawer.debug.drawer.api.DebugOnlyKeyListener

class DebugOnlyKeyListenerImpl(val application: Application) : DebugOnlyKeyListener {
    private var count = 0

    override fun onKeyDown(event: KeyEvent) {
        if (event.isVolumeDownPressed()) {
            count++
        } else {
            count = 0
        }
        // Look for volume down twice in a row.
        if (count >= 2) {
            count = 0

            // start the debug activity
            val intent = Intent(application, DebugDrawerActivity::class.java)
            intent.flags += Intent.FLAG_ACTIVITY_NEW_TASK
            application.startActivity(intent)
        }
    }

    private fun KeyEvent.isVolumeDownPressed(): Boolean {
        return keyCode == KeyEvent.KEYCODE_VOLUME_DOWN &&
                (action == KeyEvent.ACTION_DOWN || action == KeyEvent.ACTION_UP)
    }
}

While a fairly trivial implementation, we are just listening for 2 consecutive volume down presses to trigger some functionality.

Now, we can add an Activity to the :debug-drawer module.

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material3.Text

class DebugDrawerActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Text("Hello from debug drawer!")
        }
    }
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application>
        <activity
            android:name=".DebugDrawerActivity"
            android:launchMode="singleInstance" />
    </application>
</manifest>

Lastly, this can all be tied together in the main Activity that you want to trigger the debug drawer from.

Now, to test a debug build!

The debug Activity correctly opens in a debug build!

Now to test a release build.

In a release build, nothing happens after pressing the volume down two times.

At this point, you can add any functionality you want to your DebugDrawerActivity like listing feature flags, modifying and viewing network requests, etc.