Easy Navigation with Bookmarks

03 Oct 2020 | tags: android studio

One of the things I find most challenging when learning a new part of a codebase is navigating through the data flow. In some areas of our app, following RX streams can be particularly… draining.

After following all the subscribers and the observers and how things get from one place to the next, going back and doing everything all over again when I need to debug something gets a bit tedious.

Sure I can ⌘+[ / ⌘+] but sometimes there are things in the history that I don’t really care for (I’m bound to eventually get sidetracked for sure). This is when bookmarks shine for me.

Bookmarks, as the name implies, are markers that make it easy to go back to a particular line of code. Bookmarks can be set on the line where the caret is via F3 or by right-clicking on the gutter and selecting “Set Bookmark”.


Adding a bookmark

Hit ⌘+F3 to open the Bookmarks dialog to see all available options for managing bookmarks. Here we can edit descriptions for each bookmark to tell them apart, sort or re-order bookmarks, remove an existing bookmark, or check the code preview.


The Bookmarks dialog

There is no default keyboard shortcut assigned for moving through the bookmarks in order, but assigning one is pretty straightforward. Go to Preferences > Keymap > Main Menu > Navigate > Bookmarks, right click on “Next Bookmark” or “Previous Bookmark” and choose “Add Keyboard Shortcut”.


Assign a keyboard shortcut

I have chosen to use ⌘+Shift+F3 (go to next bookmark) / ^+Shift+F3 (go to previous bookmark) for my shortcuts.

I have lost count of the times that I know I have seen something before and just can’t remember where. Nowadays when I am trying to learn an unfamiliar part of our codebase, I have made it a habit to drop bookmarks on the most relevant parts of the code. Definitely saved me a lot of frustration!


Accurate Measurements With getTextBounds()

06 Aug 2020 | tags: android

We have a few custom spans in our app and over the last few days I have been poring over one of them. I was trying to see if the implementation could be improved but before that could happen I needed to understand what it was trying to do first.

This particular span, among other things, deals with drawing some text on a Canvas. We provide some styling information – text size, the text colour, and font – and in the process of drawing there’s a lot of measurements and maths.


Me

We use this span when we want to display a bunch of text where different parts may have differing font sizes. Something similar to this:

Under normal circumstances, we could use RelativeSizeSpan to set the font size. Doing that however, both words would be drawn referencing the same baseline (more on that later):

We want the word with the smaller font size to be centred vertically in relation to the rest of the text, so we need to figure out where to draw it. That, then, is the main purpose of our custom span. Unfortunately, our piece of code that’s currently doing this is not very well documented and the TextPaint documentation is very… sparse.

Hold your horses

Before we get in too deep about the implementation, here’s a quick primer on some of the terms in typography:


Source (Quora)

In other words, we want to figure out how we should adjust the baseline of the text in our span so we can tell Android where we want it to be drawn. To figure this out, the code calls TextPaint.getTextBounds().

open fun getTextBounds(text: String!, start: Int, end: Int, bounds: Rect!): Unit

In this post we will talk about what this method does but more importantly about what it means.

And let them go

Official Android documentation says the following:

Retrieve the text boundary box and store to bounds. Return in bounds (allocated by the caller) the smallest rectangle that encloses all of the characters, with an implied origin at (0,0).

… and it did not mean anything to me. :sweat_smile:

Now that we know some of the key terms in typography, let’s look at what this method tells us when we give it a piece of text. Let’s take “NEW” from the string above, for example.

The method requires several values:

The Javadoc says that the caller (me, Zarah) should allocate the Rect and then give it to getTextBounds():

val textBounds = Rect() // allocate the Rect
val text = "NEW" // text we want to measure
val start = 0 // position of the character to start with (N)
val end = 3 // position+1 of character to end with (W)
textPaint.getTextBounds(text, start, end, textBounds)

And now our textBounds will contain a bunch of values. Remember that the font and the font size affects how much space we need to draw our text, and this is what I got for a custom font and size of 10sp:

Rect(
    bottom = 0
    left = 2
    right = 66
    top = -20
    )

Turned into a photo:

The "implied origin at (0,0)" the Javadoc refers to is the bottom-leftmost corner of the baseline and the values in our textBounds are relative to this point (not to be confused with the Canvas where (0, 0) is the top-leftmost corner). Since our text is all caps, the bottom coordinate of our bounding box aligns with the baseline (how convenient). Values in textBounds increase as you go down and to the right of the origin, and decrease as you go the opposite way.

It is important to note here that the text may not be drawn exactly over the origin; there may be a leading space between the first letter and the leftmost edge of our bounding box, just like in our example here. (I don’t know enough about typography but from my experiments it looks like this depends on how the font renders each letter).


Convenience, sort of

Displaying NEW is all fine and good, but what if we give it “Something”? We now have a mix of lower- and uppercase letters, as well as letters with ascenders (things that go up) and descenders (things that go down – but in my head I call them tails).

With the same custom font and size, our textBounds now look like this:

Rect(
    bottom = 6
    left = 1
    right = 133
    top = -20
    )

Turned into a photo:

Now that we have enough information about how far down from the baseline our descent line is (0 to bottom) and how far up from the baseline our ascent line is (0 to top) (look at us using typography terms! Go us!), we can do the necessary maths to tell Android where exactly we want our text to be drawn with respect to the Canvas.

In our specific case, given our custom font and the font size, we came up with this formula:

val textY = ((bottomOfBg - textBounds.height()) / 2F) + // half of remaining space in the bg unoccupied by text
    (0F - textBounds.top) + // distance from top of text to baseline of text
    (textBounds.bottom / 2F) // half of "tails"

Purple (0,0) is the Canvas origin
Green (0,0) is the text origin
Grey outline is textBounds for each word

Some notes:

If you want to know more about Android typography, here is an excellent article about it.


Many many thanks to Ataul Munim, Florina Muntenescu, and Mike Evans for the reviews!


//TODO Live Templates

06 Mar 2020 | tags: android studio

Throughout my career, I have worked in projects of all sizes. I have taken part in greenfield projects and some that are a few years old. One of the lessons I have learned over the years is that no one ever goes back to fix the TODOs.

In our current project, we are trying to mitigate the unchecked growth of this list (we have some from 2015 :sob:). One of the solutions we are trying to explore is to create labeled TODOs:

// TODO-Zarah (06 Mar 2020): Some comments go here

This means that when leaving a TODO, devs would have to leave their name and the date in the comment. We then do periodic checks (usually before a release) to make sure that we are actively going back and actually fixing the issues.

Now typing the same thing over and over is indeed very annoying,

To help alleviate the pain, we decided to employ parameterised live templates. There are a whole bunch of these templates (Preferences > Editor > Live Templates) available in Android Studio, like the one for making a Toast:


Never forget .show() again

To create our template, open Preferences > Editor > Live Templates then click on the + sign on the right of the panel. I chose to create a Template Group to contain our custom templates, but it’s also fine to directly add a new item to any of the existing groups.


Template creation menu

Live Template Anatomy

Android Studio will ask for some information when creating templates:


Live template anatomy

Abbreviation: What the user should type to use a template
Description: Short text to appear in the context menu
Template text: The actual template, including variables
Context: Gives Android Studio hints on when it should suggest the template (choosing Java and Kotlin is usually sufficient)

Variables

Variables are either pre-defined values or input fields. In our case, we have several of these:

To auto-populate the variable values, click on the Edit Variables button and define the expressions to use. There are a lot of pre-defined functions we can leverage.


Variable editing

On my work computer, the user() function gives back my work-imposed ID and it’s not pretty. To use a better (i.e., my actual name) value without having to type it over and over, we can override Android Studio’s custom properties (Help > Edit custom properties) and add the value:

-Duser.name=Zarah

And here’s our brand new template in action:


Custom template in action

Filtering TODOs

At the start of this post I mentioned that we do periodic checks on our TODOs. When I look at our TODO panel (View > Tool Windows > TODO), there are hundreds of them across so many files. Before a release, we review all the templated ones so we can follow up with the devs who left the comments.

To make this task easier, we created a TODO filter:


TODO filters

Unfortunately the results of this search is not exportable, so if we want to generate a report we need to run code analysis (Analyze > Run inspection by name) and choose TODO. This can be exported to either HTML or XML which makes it easier to share with the team.