turbolinks/turbolinks-android

Name: turbolinks-android

Owner: Turbolinks

Description: Native Android adapter for building hybrid apps with Turbolinks 5

Created: 2016-01-25 21:03:49.0

Updated: 2018-05-24 21:24:26.0

Pushed: 2018-02-24 23:36:17.0

Homepage:

Size: 409

Language: Java

GitHub Committers

UserMost Recent Commit# Commits

Other Committers

UserEmailMost Recent Commit# Commits

README

Turbolinks Android

Build Status on Travis: Download

Turbolinks Android is a native adapter for any Turbolinks 5 enabled web app. It's built entirely using standard Android tools and conventions.

This library has been in use and tested in the wild since November 2015 in the all-new Basecamp 3 for Android.

Our goal for this library was that it'd be easy on our fellow programmers:

Contents
  1. Installation
  2. Getting Started
  3. Advanced Configuration
  4. Running the Demo App
  5. Contributing
Installation (One Step)

Add the dependency from jCenter to your app's (not project) build.gradle file.

sitories {
jcenter()


ndencies {
compile 'com.basecamp:turbolinks:1.0.8'

Getting Started (Three Steps)
Prerequisites
  1. We recommend using Turbolinks from an activity or an extension of your activity, like a custom controller. This library hasn't been tested with Android Fragments (we don't use them). We'd recommend avoiding Fragments with this library, as they might produce unintended results.
  2. Android API 19+ is required as the minSdkVersion in your build.gradle.
  3. In order for a WebView to access the Internet and load web pages, your application must have the INTERNET permission. Make sure you have <uses-permission android:name="android.permission.INTERNET" /> in your Android manifest.
1. Add TurbolinksView to a Layout

In your activity's layout, insert the TurbolinksView custom view.

TurbolinksView extends FrameLayout, so all of its standard attributes are available to you.

earLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<com.basecamp.turbolinks.TurbolinksView
    android:id="@+id/turbolinks_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

nearLayout>
2. Implement the TurbolinksAdapter Interface

Implement the TurbolinksAdapter interface in your activity, which will require implementing a handful of callback methods. These callbacks are outlined in greater detail below.

Right off the bat, you don't need to worry about handling every callback, especially if you're starting off with a simple app. Most can be left as empty methods for now.

But at the very minimum, you must handle the visitProposedToLocationWithAction. Otherwise your app won't know what to do when a link is clicked inside a WebView.

Beyond this README, you can get a good feel for the callbacks from the Javadoc and the demo app.

3. Get the Default TurbolinksSession and Visit a Location

From your activity that implements the TurbolinksAdapter interface, here's how you tell Turbolinks to visit a location:

rride
ected void onCreate(Bundle savedInstanceState) {
// Standard activity boilerplate here...

// Assumes an instance variable is defined. Find the view you added to your
// layout in step 1.
turbolinksView = (TurbolinksView) findViewById(R.id.turbolinks_view);

TurbolinksSession.getDefault(this)
                 .activity(this)
                 .adapter(this)
                 .view(turbolinksView)
                 .visit("https://basecamp.com");

?Congratulations, you're using Turbolinks on Android! ?

Advanced Configuration
Handling Adapter Callbacks

The TurbolinksAdapter class provides callback events directly from the WebView and Turbolinks itself. This gives you the opportunity to intercept those events and inject your own native actions – things like routing logic, displaying UI elements, and error handling.

As mentioned earlier, you must implement visitProposedToLocationWithAction, or your app won't know what to do when a link is clicked inside a WebView.

You can of course choose to leave the rest of the adapter callbacks blank, but we'd recommend implementing the two error handling callbacks (onReceivedError and requestFailedWithStatusCode) for when things go wrong.

visitProposedToLocationWithAction

This is a callback from Turbolinks telling you that a visit has been proposed and is about to begin. This is the most important callback that you must implement.

This callback provides your app the opportunity to figure out what it should do and where it should go. At the very minimum, you can create an Intent to open another Activity that fires another Turbolinks call with the provided location, like so:

nt intent = new Intent(this, MainActivity.class);
nt.putExtra(INTENT_URL, location);
.startActivity(intent);

In more complex apps, you'll most likely want to do some routing logic here. Should you open another WebView Activity? Should you open a native Activity in certain cases? This is the place to do that logic.

onPageFinished

This is a callback that's executed at the end of the standard WebViewClient's onPageFinished method.

This callback will only be fired once upon cold booting. If there is any action you need to take after the first full page load is complete, just once, this is the place to do it.

The reason this is only called once is because the first location that Turbolinks loads after initialization is always a “cold boot” – a full page load of all resources that's executed through a normal WebView.loadUrl(url). Every subsequent location visit (with the exception of an error condition or page invalidation) will fire through Turbolinks, without a full page load.

visitCompleted

This is a callback from Turbolinks telling you it considers the visit completed. The request has been fulfilled successfully and the page fully rendered.

It's similar conceptually to onPageFinished, except this callback will be called for every Turbolinks visit. This is a good time to take actions that you need on every page, such as reading data-attributes (or other metadata) from the loaded page.

onReceivedError

This is a callback that's executed at the end of the standard WebViewClient's onReceivedError method.

We recommend you implement this method. Otherwise, your user will see an endless progress view/spinner without something that handles the error. You can handle the error however you like – send the user to a different page, show a native error screen, etc.

requestFailedWithStatusCode

This is a callback from Turbolinks telling you that an XHR request has failed.

We recommend you implement this method. Otherwise, your user will see an endless progress view/spinner without something that handles the error. You can handle the error however you like – send the user to a different page, show a native error screen, etc.

pageInvalidated

This is a callback from Turbolinks telling you that a change has been detected in a resource/asset in the <HEAD>, and as a result the Turbolinks state has been invalidated. Most likely the web app has been updated while the app was using it.

The library will automatically fall back to cold booting the location (which it must do since resources have been changed) and then will notify you via this callback that the page was invalidated. This is an opportunity for you to clean up any UI state that you might have lingering around that may no longer be valid (title data, etc.)

Overriding Default TurbolinksSession Settings

There are some optional features in TurbolinksSession that are enabled by default.

Bitmap Screenshots

When the Turbolinks WebView is detached from an activity, a bitmap screenshot of the view is automatically created and later displayed when the activity is resumed - until a copy of the page is restored from cache in the WebView. This gives the illusion that the WebView was never detached and no visual flicker is seen. To disable this behavior, simply call:

olinksSession.setScreenshotsEnabled(false);
Pull To Refresh

Refreshes the TurbolinksView when a user swipes down from the top of the view. To disable simply call:

olinksSession.setPullToRefreshEnabled(false);
Custom Instance(s) of TurbolinksSession

We provide a single, reusable instance of TurbolinksSession that you can access through this convenience method:

olinksSession turbolinksSession = TurbolinksSession.getDefault(context);

If you need greater control, you can always create your own instance(s) with:

olinksSession myTurbolinksSession = TurbolinksSession.getNew(context);

Some things to keep in mind if you create your own instance of TurbolinksSession:

You'll need to weigh the benefits and complexities of those options, but the bottom line is that you'll want to carefully manage the lifecycle of your Turbolinks instance(s).

Custom Progress View

By default the library will provide you with a progress view with a progress bar – a simple FrameLayout that covers the WebView while it's loading, and shows a spinner after 500ms.

If that doesn't meet your needs, you can also pass in your own custom progress view like so:

olinksSession.getDefault(this)
             .activity(this)
             .adapter(this)
             .progressView(progressView, resourceIdOfProgressBar, progressBarDelay)
             .view(turbolinksView)
             .visit("https://basecamp.com");

Some notes about using a custom progress view:

Custom WebView WebSettings

By default the library sets some minimally intrusive WebSettings on the shared WebView. Some are required while others serve as reasonable defaults for modern web applications. They are:

If however these are not to your liking, you can always override them. The WebView is always available to you via getWebView(), and you can update the WebSettings to your liking:

ettings settings = TurbolinksSession.getDefault().getWebView().getSettings();
ings.setDomStorageEnabled(false);
ings.setAllowFileAccess(false);

If you do update the WebView settings, be sure not to override setJavaScriptEnabled(false). Doing so would break Turbolinks, which relies heavily on JavaScript.

Custom JavascriptInterfaces

If you have custom JavaScript on your pages that you want to access as JavascriptInterfaces, you can add them like so:

olinksSession.getDefault().addJavascriptInterface(this, "MyCustomJavascriptInterface");

The Java object being passed in can be anything, as long as it has at least one method annotated with @android.webkit.JavascriptInterface. Names of interfaces must be unique, or they will be overwritten in the library's map.

Running the Demo App

A demo app is bundled with the library, and works in two parts:

  1. A tiny Turbolinks-enabled Sinatra web app that you can run locally.
  2. A tiny Android app that connects to the Sinatra web app.
Prerequisites
Start the Demo Sinatra Web App

You should see a message saying what port the demo web app is running on. It usually looks like:

Listening on 0.0.0.0:9292

Start the Demo Android App
Contributing

Turbolinks Android is open-source software, freely distributable under the terms of an MIT-style license. The source code is hosted on GitHub.

We welcome contributions in the form of bug reports, pull requests, or thoughtful discussions in the GitHub issue tracker. Please see the Code of Conduct for our pledge to contributors.

Turbolinks Android was created by Dan Kim and Jay Ohms, with guidance and help from Sam Stephenson and Jeffrey Hardy. Development is sponsored by Basecamp.

Building from Source
From Android Studio: From command line:

The .aar's will be built at <project-root>/turbolinks/build/outputs/aar.

Running Tests

From command line:


This work is supported by the National Institutes of Health's National Center for Advancing Translational Sciences, Grant Number U24TR002306. This work is solely the responsibility of the creators and does not necessarily represent the official views of the National Institutes of Health.