Creating a Simple browser with Mozilla Android Components.

At Mozilla, we have multiple teams that work on different Android browsers like Focus, Firefox for Amazon fire TV, Firefox Amazon’s Echo Show, and Rocket. Those are all are browsers with different use cases but with similar behavior in common, with the goal of sharing functionality across products and rapid iteration creating new browsers the Android Components project was born.

The Android Components is a set of reusable pieces of software that allows you to mix and match different components to create browsers without having to reinvent the wheel every time.

In simple, words Android Components is a set of Lego blocks that you can easily combine together to create browsers. We have a large array of components to cater different needs from UI to bare bone interfaces to allow you to integrate your own implementations for completed flexibility.

Android Components is a project in continuous development, we have many components that are in active development, and many more that are coming. You can know the status of each component, for the prefix-colored circle in front of the name of each component.

Components status.

For this blog post, we’re going to focus our attention on creating a Simple browser using Android Components. All the source code is available on this GitHub repository, for every step that we achieve there is separated branch, this way you can see the diff between each step.

Imagine that you want to create a new browser and you need a toolbar and a web view to render content, instead of creating everything from scratch and doing the hard work, a wise choice could be to use browser-toolbar, browser-engine-gecko, browser-engine-servo or browser-engine-system components, that have all the behavior that you are going to need pre-baked in.

To have a bit of context, let’s dive in what a web browser does and how we can achieve the same using Components.

What’s a web browser?

A browser is an application that allows us to browse web pages. Every time that you access an URL many things happened behind the scene:

A basic representation of what a browser does.
  1. Imagine that you want to go to www.mozilla.org. You type the URL www.mozilla.org on the ToolBar.

2. After you hit the enter key, this URL goes through a loading process that involves fetching the URL, downloads the associated content normally an HTML file then, this content goes to a web engine who is responsible for interpreting these files (rendering) and giving us back our shiny web page.

Note: The HTML file is the skeleton of our web page, it contains everything that will define how our web page is layout (HTML tags), looks (CSS) and behave (JavaScript).

Let’s see how we can do the same with Android Components.

Let’s get our hands dirty!

The first thing that we’re going to need is a ToolBar where we can type URLs.

1.ToolBar (Source Code).

A toolbar is a UI component that allows us to capture user typing interactions, like handling an URL, showing suggestions or trigger a search.

Basic toolbar.

Also It can be a container for other widgets, that help the user to perform common tasks like navigating back and forward or reloading the actual page.

Toolbar with buttons.

Luckily for us, the Android Components team has done all the heavy lifting 🏋️‍♀️ for us, building a component called browser-toolbar that allow us to just include a toolbar widget and customize it to our needs. By the way, all these nice images above are from it!

To include any component first, we need to add the Maven Mozilla URL to our repositories block in the root build.gradle file.

repositories {
maven {
url "https://maven.mozilla.org/maven2"
}
}

And add the browser-toolbar component dependency:

app/build.gradle

implementation "org.mozilla.components:browser-toolbar:{latest-version}"

You can find the latest version of components here.

Note: All the component are segregate by modules, this way you only need to include just what you need 😉.

Now we can add our toolbar ui.

activity_main.xml

<mozilla.components.browser.toolbar.BrowserToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>

Now our browser has a ToolBar 😍 yay!!!

🤔 wait a minute, the URL doesn’t load 😅, lets the toolbar does the right thing.

Note: You can find more advanced uses and examples of a toolbar in our Toolbar Sample app.

2. Web Engine (Source Code)

As I mentioned above in a web browser the component that takes cares of taking a URL and renders it into a website is called web engine. There are multiple web engines implementations but at the moment, Android Components offers support for Gecko, Servo, and Blink(Android Web View).

Android Components offers an abstraction layer called concept-engine (API Docs) that is a set of interfaces that allow us to switch between web engine implementations without getting crazy in the process 🤪.

Let’s add concept-engine dependency

app/build.gradle

implementation "org.mozilla.components:concept-engine:{latest-version}"

Also let’s add a concrete web engine implementation, in this case Gecko 🦎.

Follow the steps to include the dependency for GeckoView 🦎 and add component browser-engine-gecko

app/build.gradle

implementation "org.mozilla.components:browser-engine-gecko:{latest-version}"

activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
    <mozilla.components.browser.toolbar.BrowserToolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="56dp"
android:background="#aaaaaa"
/>
    <mozilla.components.concept.engine.EngineView
android:id="@+id/engineView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
/>
</LinearLayout>

Now to render the URL.

MainActivity.kt

class MainActivity : AppCompatActivity() {
    private val engine: Engine by lazy {
GeckoEngine(this)
}
    override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
        val session = engine.createSession()
engineView.render(session)
session.loadUrl("https://mozilla.org")
}
    override fun onCreateView(parent: View?, name: String?, context: Context, attrs: AttributeSet?): View? =
when (name) {
EngineView::class.java.name -> engine.createView(context, attrs).asView()
else -> super.onCreateView(parent, name, context, attrs)
}
}
GeckoEngine (GeckoView 🦎)

Hooray 😎!

But there is more 😉, you can switch between engines implementation.

Let’s switch to an Android Web View(Blink)

implementation "org.mozilla.components:browser-engine-gecko:{latest-version}"

And replace the instance of engine by SystemEngine one!

MainActivity.kt

private val engine: Engine by lazy {
SystemEngine(this)
}

And it just work 😛!

SystemEngine implementation (Android WebView)

This is the real power 💪 of Android Component, we do the 🏋️‍♀️ hard work for you!

Back to the business, let’s connect the toolbar and Engine View together 👌!

3.Toolbar + Web Engine = Feature (Source Code)

Now the main idea is to connect both of them together, this way every time that we enter an URL in the toolbar it loads in the EngineView.

We could do it manually observing the changes in the toolbar and loading the typed URL on the EngineView, but we have a smarter choice 🤓. For cases like this one, we have the concept of features, these are the combination of multiple components that achieve certain functionality together.

There are two features that do exactly what we need 🤩 feature-toolbar and feature-session that just does exactly what we need.

Let’s add the feature-toolbar dependency

implementation "org.mozilla.components:feature-toolbar:{latest-version}"

We also we going to need the feature-session(API Docs) component

implementation "org.mozilla.components:feature-session:{latest-version}"

Now let’s integrate feature-toolbar and feature-session to MainActivity.kt

class MainActivity : AppCompatActivity() {
/*...*/
    private lateinit var toolbarFeature: ToolbarFeature
private lateinit var sessionFeature: SessionFeature
    private lateinit var sessionUseCases: SessionUseCases
    override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
        sessionManager = SessionManager(engine)
sessionUseCases = SessionUseCases(sessionManager)
        sessionFeature = SessionFeature(
sessionManager,
sessionUseCases,
engineView
)
        toolbarFeature = ToolbarFeature(
toolbar,
sessionManager,
sessionUseCases.loadUrl
)
        //Adding a default session
sessionManager.add(Session("https://www.mozilla.org"))
    }
    override fun onStart() {
super.onStart()
toolbarFeature.start()
sessionFeature.start()
}
    override fun onStop() {
super.onStop()
toolbarFeature.stop()
sessionFeature.stop()
}
    override fun onCreateView(parent: View?, name: String?, context: Context, attrs: AttributeSet?): View? =
when (name) {
EngineView::class.java.name -> engine.createView(context, attrs).asView()
else -> super.onCreateView(parent, name, context, attrs)
}
}

Now we have our simple browser up and running 🕺 💃!

Navigation Buttons (Source Code)

To add the cherry on top 🍰, let’s add the typical navigation buttons back ◀️, forward ▶️ and reload ♻️.

For this let’s use the browser-menu component that allow us to create customizable items for our toolbar.

implementation "org.mozilla.components:browser-menu:{latest-version}"

Also, let’s include the ui-icons and ui-colors components that help us to style our toolbar with icons and colors from the Firefox design language photon.

implementation "org.mozilla.components:ui-icons:{latest-version}"
implementation "org.mozilla.components:ui-colors:{latest-version}"

And Add this items to MainActivity.kt

class MainActivity : AppCompatActivity() {
        /*...*/
override fun onCreate(savedInstanceState: Bundle?) {

/*...*/
        toolbar.setMenuBuilder(getBrowserMenuBuilder())
}
/*...*/
    private fun getBrowserMenuBuilder(): BrowserMenuBuilder {
return BrowserMenuBuilder(listOf(getBrowserMenuItemToolbar()))
}
    private fun getBrowserMenuItemToolbar(): BrowserMenuItemToolbar {
        val back = BrowserMenuItemToolbar.Button(
mozilla.components.ui.icons.R.drawable.mozac_ic_back,
iconTintColorResource = R.color.photonBlue90,
contentDescription = "Back") {
sessionUseCases.goBack.invoke()
}
        val forward = BrowserMenuItemToolbar.Button(
mozilla.components.ui.icons.R.drawable.mozac_ic_forward,
iconTintColorResource = R.color.photonBlue90,
contentDescription = "Forward") {
sessionUseCases.goForward.invoke()
}
        val refresh = BrowserMenuItemToolbar.Button(
mozilla.components.ui.icons.R.drawable.mozac_ic_refresh,
iconTintColorResource = R.color.photonBlue90,
contentDescription = "Refresh") {
sessionUseCases.reload.invoke()
}
        val stop = BrowserMenuItemToolbar.Button(
mozilla.components.ui.icons.R.drawable.mozac_ic_stop,
iconTintColorResource = R.color.photonBlue90,
contentDescription = "Stop") {
sessionUseCases.stopLoading.invoke()
}
        return BrowserMenuItemToolbar(listOf(back, forward, refresh, stop))
}
}
Simple browser with Navigation buttons.

We did it 🙌!

Bender supporting Android Components

This is only a small example of what you can do with Mozilla Android Components, take a look at our Sample apps where you can find many more advanced use cases and components in action.

We’re an open source project help is definitely welcome! You can find information about how to contribute here, and stay up to date by joining our mailing list.

Hope you like this blog post, any feedback or questions are more than welcome!