The Bikeshop

Pedaling steadily toward cleaner code.

Reddit
LinkedIn

When I joined the Developers team at Square a few weeks ago, I was told that, as part of onboarding, every new team member makes a sample application using our APIs. Rich sample applications are great because they not only show developers how the API works, but also spark ideas about the variety of complex things developers can do with our tools. Making these sample apps gives us an opportunity to gain firsthand experience with the APIs, as well as feel the same pain points as our developers and provide the team with valuable feedback about the product.

Original designs for the bikeshop used for Register API promotionOriginal designs for the bikeshop used for Register API promotion

I developed a sample application called the Bikeshop. Our promotional materials for the API show a fictional point-of-sale app for a custom-built bike shop named Owen’s Bikes—we decided to bring these two-dimensional promo images to life and build a real application.

The main screen features a bike that the user can customize with a number of hardware parts and accessories. The checkout button triggers the Register Android SDK, which takes the customer into Square Register to complete the transaction. After the transaction completes, the result is sent back to the Bikeshop by the API and is used to display the “Order Complete” page.

Creating the Bikeshop was a valuable experience for a number of reasons, including developing very quickly and then iterating until the code was open-source ready. This iteration was a really fun process because it involved cleaning up my code—and who doesn’t love clean code? I learned (or reinforced) a few things through the process of designing and improving this application with my knowledgeable new coworkers, and while they’re pretty basic concepts, I’m hopeful they’ll help someone on their quest for an Android application full of squeaky-clean code.

Bikeshop screenshot in landscapeBikeshop screenshot in landscape

Abstract Away Complexity

One tricky aspect of our designs was the changing layout of the two displayed lists, labeled “Bike” and “Accessories” (labeled “modifiers” and “accessories” in the application). As demonstrated in the screenshots, there are two lists side-by-side in portrait, while in landscape they’re stacked.

My first thought was to create two separate lists and just stack them in landscape — but something that looks like one list but scrolls in two places would create a bad user experience. With that out of the question, I was stuck with creating two lists in portrait and one in landscape.

How to set this up? Well, fragments were out because Py ⚔ advocates against them. So I decided to use several RecyclerViews for the list(s). They would all be populated with a single adapter class, which wouldn’t know what kind of list it was talking to. In the main activity, I kept three RecyclerAdapters and used one or two of them depending on the orientation.

Sounds simple enough, right? Right. Here’s what my initial code looked like to set up the views and adapters.

private void setupAdapters() {
   if (isLandscape()) {
     RecyclerView bikeItemList = (RecyclerView) findViewById(R.id.bike_item_list);
     bikeItemAdapter = createAdapterWithListeners();
     bikeItemList.setAdapter(bikeItemAdapter);
     bikeItemList.setLayoutManager(getVerticalLayoutManager());
     bikeItemList.setVerticalFadingEdgeEnabled(true);
   } else {
     // In portrait, use separate recyclerviews and adapters for bike modifiers and accessories.
     RecyclerView modifierList = (RecyclerView) findViewById(R.id.modifier_list);
     modifierAdapter = createAdapterWithListeners();
     modifierList.setAdapter(modifierAdapter);
     modifierList.setLayoutManager(getVerticalLayoutManager());
     modifierList.setVerticalFadingEdgeEnabled(true);

     accessoryList = (RecyclerView) findViewById(R.id.accessory_list);
     accessoryAdapter = createAdapterWithListeners();
     accessoryList.setAdapter(accessoryAdapter);
     accessoryList.setLayoutManager(getVerticalLayoutManager());
     accessoryList.setVerticalFadingEdgeEnabled(true);
   }
 }

This clogged up the main activity with a lot of confusing boilerplate—not a great example for our developers.

What’s more, any time the activity talked to the adapter, the orientation had to be checked again to make sure the right adapter was used. More annoying if/else branches like this:

or this:

BikeItemAdapter adapter = isLandscape() ? bikeItemAdapter : modifierAdapter;
adapter.addRows(bikeModifierRows);

The first change to start fixing this was to move the code that talked to the adapters into a new class called the ItemManager. The goal here was to leave the main activity with as few responsibilities as possible, because that’s just good practice: see the Single Responsibility Principle.

Next step: The ItemManager shouldn’t need to know whether the device is in landscape or portrait to call a method on the adapter. This begged for an interface, so we made one:

public interface AdapterController {

  void setupAdapters();

  BikeItemAdapter getAccessoryAdapter();

  BikeItemAdapter getModifierAdapter();

  int getTotal();

  String getNote();

  void setListener(BikeItemAdapter.UpdateListener listener);
  
}

We implemented this interface with two classes, AdapterController.Landscape and AdapterController.Portrait, to get the desired behavior. Now when the ItemManager wants to get the note, total, or adapter it can just call the appropriate interface method. The orientation is only checked once, when the main activity creates the static AdapterController to pass to the ItemManager.

boolean isLandscape = getResources().getConfiguration().orientation == ORIENTATION_LANDSCAPE;
AdapterController adapterController = isLandscape 
    ? new AdapterController.Landscape(this)
    : new AdapterController.Portrait(this);

No more need for that isLandscape() method we were calling all the time, and all the confusing if/else statements could be deleted.

Immutability FTW

In the app, we load the modifiers and accessories from a static catalog. Each item has a list of options. For example, the item “Frame Size” contains options for the size of the frame, from “42 cm” to “62 cm”. The set of options never changes, but the user can change which option they’ve selected. Originally this was reflected in my item model class:

  private final String id;
  private final String name;
  private final Category category;
  private final List<Option> options;
  private Option selectedOption;

  BikeItem(String id, String name, Category category, List<Option> options) {
    this.id = id;
    this.name = name;
    this.category = category;
    this.options = options;
  }
  
  public Option getSelectedOption() {
    return selectedOption;
  }

  public void setSelectedOption(Option variation) {
    this.selectedOption = variation;
  }

  public String getId() {
    return id;
  }

…and on and on with getter methods. After a bit of review, however, we noticed a few things that could be improved about this model object. First, the getters and setters were not necessary. What I thought was best practice was leading to unnecessary method calls elsewhere in the app. Second, it looked a lot like it should be an immutable object. The same items were read from the same catalog every time, and nothing about them ever changed except the currently selected option. The warning sign here was that all but one of the fields were final. So, we decided to save the selected options in the ItemManager and let the BikeItem class be an immutable object representing an item in the catalog, which was its original purpose.

It ended up looking like this:

  public final String id;
  public final String name;
  public final Category category;
  public final List<Option> options;

  BikeItem(String id, String name, Category category, List<Option> options) {
    this.id = id;
    this.name = name;
    this.category = category;
    this.options = options;
    this.options = Collections.unmodifiableList(options);
  }

Much cleaner.

Avoid Duplicating String Constants

Here’s another fun cleanup. When the bikeshop receives a ChargeResult from the Register API, it takes the ORDERNUMBER String from the Intent and passes it in a new Intent to start the TransactionSuccessActivity. The TransactionSuccessActivity then extracts the ORDERNUMBER String from the Intent to display it to the user. This pattern resulted in the ORDER_NUMBER constant being defined in two places. Unsightly.

Since the TransactionSuccessActivity was the one displaying the order number, it should be responsible for the string constant. So we removed the String from MainActivity and put it there. MainActivity still needed to pass the String along, so we created a static method in TransactionSuccessActivity.

private static final String ORDER_NUMBER = "ORDER_NUMBER";

public static void start(Context context, String orderNumber) {
  Intent intent = new Intent(context, TransactionSuccessActivity.class);
  intent.putExtra(ORDER_NUMBER, orderNumber);
  context.startActivity(intent);
}

Then MainActivity just calls TransactionSuccessActivity.start() with the context and order number value and doesn’t need to know about the String ORDER_NUMBER. This is a really simple change that removes duplication, prevents future bugs, and makes the MainActivity look even cleaner.

Conclusion

There were countless improvements to the quality of this code as we iterated. They’re not all interesting, but they certainly helped me get my feet wet with Android while learning about the quality of code that’s expected of a Square engineer. Of course, there’s still plenty of additional clean-up potential—not to mention countless features to add! Allowing users to customize the colors for each piece of the bike, or obtaining images of different bike parts so that we could swap them out when a user makes a selection, would enhance the app experience. But as much as I would love to keep improving Bikeshop, there’s work to be done on our APIs!

You can find the Bikeshop sample app on GitHub with the Register Android SDK, and there are instructions for building the app so you can start playing around with it right away. Feedback and contributions are welcome. Also available on GitHub is an APK for download, so you can use the application without needing a Square developer account.

We hope that this is just the first of many full-fledged sample applications for our developers. We’re optimistic that someone can use Bikeshop as a jump-off point for their own awesome point of sale. Show us what you can do!