Rebranding OneBusAway Android for Your City

September 6, 2026 · View on GitHub

There are two ways to deploy OneBusAway Android in your city:

  1. Join the OneBusAway multi-region project - The easiest way to get started - simply set up your own OneBusAway server with your own transit data, and get added to all the OneBusAway apps! See this page for details.
  2. Deploy a rebranded version of OneBusAway Android as your own app on Google Play - Requires a bit more maintenance, but it allows you to set up your own app on Google Play based on the OneBusAway Android source code, and with your brand name and colors. This page discusses this option in detail.

Rebranding Using Gradle Build Flavors

We use Gradle build flavors to enable a number of different build variants of OneBusAway Android.

We have one Gradle "platform" flavor dimension:

  • google = Normal Google Play release

...and several Gradle "brand" flavor dimensions:

  • oba = Original OneBusAway brand
  • agencyX = A sample rebranded version of OneBusAway for a fictitious "Agency X"
  • agencyY = A sample rebranded version of OneBusAway for a fictitious "Agency Y"
  • kiedybus = KiedyBus, a Polish transit app

Here's where you can download three of these brands on Google Play (KiedyBus is maintained by a third party and isn't listed here):

And here are screenshots of those same three brands:

Side-by-side screenshots of the OneBusAway, Agency X, and Agency Y brands, each showing the same stop and arrival times screen in its own brand colors

Each brand is deployed as an independent app on Google Play (using the google platform flavor).

To build a variant, you need to combine the platform flavor with the brand flavor. For example, the original OneBusAway brand for the Google platform can be built with:

gradlew installObaGoogleDebug

Creating a New Brand

Brand flavors are defined in separate files in the onebusaway-android/flavors/ directory. This keeps the main build.gradle.kts clean and makes it easy to add new brands.

First, we recommend that you review the sample brands (agencyX, agencyY, kiedybus) to see how brands are implemented.

Here are the high-level steps to add a new brand, for a new brand name newBrandName:

  1. Create a new flavor configuration file onebusaway-android/flavors/newBrandName.gradle
  2. Create a new folder src/newBrandName/res with the appropriate resource files (or copy from one of the samples)
  3. Edit resource files in src/newBrandName/res subfolders with your brand information
  4. Add your own launcher icons for the res/mipmap-* folders
  5. Configure Google Maps API key for your package name
  6. Configure Firebase for analytics and crash reporting (optional but recommended)

Step 1: Create Flavor Configuration

Copy an existing flavor file as a template:

cp onebusaway-android/flavors/agencyX.gradle onebusaway-android/flavors/newBrandName.gradle

Edit the file and update:

android.productFlavors {
    newBrandName {
        dimension "brand"
        applicationId "com.newbrandname.android"  // Your unique package name
        manifestPlaceholders = [databaseAuthority: applicationId.toString() + '.provider',
                                deepLinkScheme: "newbrandname"]  // Optional: your own URL scheme
        buildConfigField "int", "ARRIVAL_INFO_STYLE", "0"
        buildConfigField "boolean", "USE_FIXED_REGION", "false"  // or "true" for single-region
        // ... other configuration options
    }
}

See onebusaway-android/flavors/README.md for the complete list of configuration options.

Step 2: Create Resource Directory

Create minimal resources in src/newBrandName/res/:

src/newBrandName/
└── res/
    ├── values/
    │   ├── strings.xml          # App name (required)
    │   ├── colors.xml           # Theme colors (required)
    │   └── do_not_translate.xml # API keys, URLs (required)
    └── mipmap-*/
    │   ├── ic_launcher.png      # App icon (all densities)
    │   └── ic_launcher_round.png
    └── values-night/
        └── colors.xml          # Dark Theme colors (required)

Step 3: Configure Resources

strings.xml

Thanks to the placeholder system, you typically only need to override app_name:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">New Brand Name</string>
</resources>

All other branded strings use %1$s placeholders that automatically substitute your app name.

colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="brand_color">#YOUR_PRIMARY_COLOR</color>
    <color name="brand_color_dark">#YOUR_DARK_COLOR</color>
    <color name="theme_muted">#YOUR_MUTED_COLOR</color>
    <color name="theme_accent">#YOUR_ACCENT_COLOR</color>
    <!-- Primary color with a "df" alpha prefix (~87% opaque) -->
    <color name="tutorial_background">#dfYOUR_PRIMARY_COLOR</color>
    <color name="ic_launcher_background">#YOUR_PRIMARY_COLOR</color>
</resources>

Do not override the stop_info_* colors. They encode schedule deviation (early / on time / late / no real-time), which is semantic rather than brand identity, and they are intentionally independent of brand_color so a brand hue can never collide two states into the same color.

To override material 3 colors, please checkout the theme builder.

do_not_translate.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="apiv2_key">YOUR_GOOGLE_MAPS_API_KEY</string>
    <!-- Only if using custom regions API: -->
    <string name="regions_api_url">https://your-domain.com/regions.json</string>
</resources>

Step 4: Generate Launcher Icons

Use one of these tools to generate icons for all densities:

Required sizes:

  • mdpi: 48x48
  • hdpi: 72x72
  • xhdpi: 96x96
  • xxhdpi: 144x144
  • xxxhdpi: 192x192

Step 5: Configure Google Maps API

Google Maps requires an API key that is restricted to your app's package name.

  1. Go to the Google Cloud Console
  2. Create a new project or select an existing one
  3. Enable the Maps SDK for Android API
  4. Go to Credentials and create an API key
  5. Restrict the key:
    • Click on the API key to edit it
    • Under "Application restrictions", select Android apps
    • Add your package name (e.g., com.newbrandname.android)
    • Add your app's SHA-1 fingerprint:
      # For debug builds:
      keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android
      
      # For release builds, use your release keystore
      
  6. Copy the API key to src/newBrandName/res/values/do_not_translate.xml

Important: You need separate SHA-1 fingerprints for debug and release builds. For production, add both fingerprints to your API key restrictions.

See Google's documentation for more details.

Firebase provides analytics and crash reporting. To set up Firebase for your brand:

  1. Go to the Firebase Console
  2. Create a new project or select an existing one
  3. Click Add app and select Android
  4. Enter your package name (e.g., com.newbrandname.android)
  5. Download the google-services.json file
  6. Important: Don't replace the existing google-services.json. Instead, add your app's client configuration to the existing file.

Adding Your App to google-services.json

Open onebusaway-android/google-services.json and append the following object to the existing client array, leaving the top-level project_info and the existing client entries untouched:

{
  "client_info": {
    "mobilesdk_app_id": "1:YOUR_PROJECT_NUMBER:android:YOUR_APP_ID",
    "android_client_info": {
      "package_name": "com.newbrandname.android"
    }
  },
  "oauth_client": [
    {
      "client_id": "YOUR_CLIENT_ID.apps.googleusercontent.com",
      "client_type": 3
    }
  ],
  "api_key": [
    {
      "current_key": "YOUR_FIREBASE_API_KEY"
    }
  ],
  "services": {
    "appinvite_service": {
      "other_platform_oauth_client": [
        {
          "client_id": "YOUR_CLIENT_ID.apps.googleusercontent.com",
          "client_type": 3
        }
      ]
    }
  }
}

You can find these values in the google-services.json file you downloaded from Firebase Console.

Note: If you don't configure Firebase, the app will still build and run, but you won't have analytics or crash reporting for your brand.

Step 7: Build and Test

./gradlew assembleNewBrandNameGoogleDebug
./gradlew installNewBrandNameGoogleDebug

Simplified String Localization with Placeholders

The app uses a placeholder system for branded strings, which significantly reduces the work needed to support multiple languages in your rebranded app.

How it works: Strings that mention the app name use %1$s as a placeholder, which is automatically replaced with your app_name at runtime. For example:

<!-- In src/main/res/values/strings.xml -->
<string name="tutorial_welcome_title">Welcome to %1$s!</string>
<string name="bad_gateway_error">%1$s\'s servers are overloaded. Please try again.</string>

What this means for you: You only need to override app_name in each language your brand supports. All other branded strings will automatically use your app name.

<!-- src/newBrandName/res/values/strings.xml -->
<resources>
    <string name="app_name">New Brand Name</string>
</resources>

<!-- src/newBrandName/res/values-es/strings.xml -->
<resources>
    <string name="app_name">New Brand Name</string>
</resources>

Optional overrides: You may still need to override specific strings if your agency has different UI elements. For example, if your agency uses red stop markers instead of green:

<!-- src/newBrandName/res/values/strings.xml -->
<resources>
    <string name="app_name">New Brand Name</string>
    <!-- Override because our stop dots are red, not green -->
    <string name="tutorial_welcome_text">Tap on a stop (red dot on the map) to see arrival times.</string>
</resources>

See the agencyX, agencyY, and kiedybus sample flavors for working examples of this pattern.

Configuration Options

We provide configuration options in the flavor .gradle files that allow you to choose default behaviors for your brand.

Arrival display

Riders choose their default arrivals display in Settings and can switch between Time (one departure per row) and Route (grouped departures) in an open arrivals drawer. The initial fallback is Route. The retired ARRIVAL_INFO_STYLE build option no longer controls this presentation.

Arrival display default in Settings

The migration picker is offered only when the recorded previous release is 26.1.0 or earlier (published version codes 1–154) and no display default has been chosen. Fresh installs, missing version history, and upgrades from later releases skip it. Eligibility is recorded before release notes update the version marker, so dismissing the picker preserves the offer for the next launch.

Fixed vs. Multi-region

USE_FIXED_REGION - Valid values are true and false:

  • false - The app works across various regions defined in the Regions API (recommended for most brands)
  • true - The app is fixed to the region information provided in the flavor configuration

When USE_FIXED_REGION is true, FIXED_REGION_NAME is required - it is the one FIXED_REGION_* field the app cannot start without, and leaving it null (the value every multi-region flavor uses) crashes on the first launch. Every other FIXED_REGION_* field is optional in the sense that it will not crash: FIXED_REGION_OBA_BASE_URL in particular is silently accepted as null, which builds and installs fine and then simply cannot reach a server — so set it too, and set the bounds, or the fixed region will not work.

When USE_FIXED_REGION is false, the FIXED_REGION_* fields are unread; leave them null as agencyX.gradle does.

Geocoding Provider

USE_PELIAS_GEOCODING - Controls which service is used for searching origins and destinations in trip planning:

  • true - Uses the Pelias geocoder (configured for geocode.earth by default). Requires a Pelias API key in gradle.properties:
    Pelias_newBrandName=YOUR_API_KEY
    
  • false - Uses the Google Places SDK. Requires a Google Maps Platform billing account.

Examples

See the following flavor files for complete examples:

Acknowledgements

When launching a rebranded version of OneBusAway, acknowledging that your app is based on the hard work of those contributing to the OneBusAway project is certainly appreciated. However, please do not imply that the OneBusAway project or its contributors endorse the rebranded app, and please do not use the OneBusAway logo or color scheme in your rebranded app.