Google AutoLogin in Webview

Google Autologin in an Android WebView

The Android browser has a nifty feature that lets you autologin to sites that use Google accounts, using accounts that you’ve already logged into on the phone (at least on Honeycomb+).  This is really convenient, because a user can just click on the “Sign in” button instead of typing in his full account name/password on a crappy mobile keyboard.

Unfortunately, this functionality did not make it into the WebView, so if you’re building you’re on WebView based app, you’re out of luck.  You gotta do it yourself.

Fortunately, Android is open source, and we can look at the Android Browser source, and get the code.  The source is hosted by the Android Open Source Project.  The browser source is here:

https://android.googlesource.com/platform/packages/apps/Browser/

The autologin function is mostly contained within two classes:

DeviceAccountLogin.java

AutologinBar.java

You’ll also need the resource file for the autologin bar:

res/layout/title_bar_autologin.xml

You may also need to copy over res/anim/autologin_enter.xml and res/anim/autologin_exit.xml if you want the autologin bar to animate in and out.  In addition, there’s a handful of strings you’ll need to pull from the Browser project’s string.xml.

Those are pretty much all the pieces needed.  Now comes the hard part, which is integrating it to your own project.

Integrating the layout was pretty easy for me.  I have a pretty simple layout that just contains my WebView and a progress bar.  In the original Browser project, the autologin bar is integrated with the title bar.  You can look in title_bar.xml and TitleBar.java to get a sense of how it’s instantiated.

All I had to do was to add the ViewStub for the autologin bar to my Layout:

<ViewStub  android:layout="@layout/title_bar_autologin"  android:id="@+id/autologin_stub"  android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="3dip" />

The harder part is the code.  The Browser is pretty well componentized into a View-Controller structure, and different parts of the UI are broken up into their own components, notably tabs and the title bar (which contains the autologin bar).

My own case was much simpler, I just have an Activity containing a WebView, so I took all the Autologin functionality related to the tab, controller, and title bar, and stuck it all in my main activity.  It actually wasn’t too messy.

First let’s go over a brief overview of how the Autologin works in the Android Browser.

  1. When the WebView browses to a Google login page, the WebViewClient receives a callback onReceivedLoginRequest() (see the Browser’s Tab.java for details).
  2. In onReceivedLoginRequest(), we instantiate DeviceAccountLogin and call handleLogin().
  3. handleLogin() calls the Controller to display the autologin UI.
  4. the Controller calls the BaseUI to display the autologin UI (BaseUI::showAutoLogin()).
  5. the BaseUI calls the TitleBar to display the autologin UI (TitleBar::updateAutoLogin()).
  6. the TitleBar instantiates the AutologinBar and calls AutologinBar.updateAutoLogin(), which is a key step.
  7. At this point the AutologinBar fetches the DeviceAccountLogin from the Tab and gets the account names.
  8. The AutologinBar calls back to the Title Bar to display the autologin bar UI (TitleBar::showAutoLogin())

In the case of my app, I didn’t have a Controller, BaseUI or TitleBar, so I merged all that functionality into my MainActivity.  The actual work involved is:

  • Inside DeviceAccountLogin.java replace references to the WebViewController and Tab to point to MainActivity.
  • Inside AutologinBar.java replace references to TitleBar and Tab to point to MainActivity.

On our MainActivity, I’ll need to implement the actual functions.  on the Controller, BaseUI, TitleBar and Tab that are actually needed.  It’s not actually much.  The one thing the be wary of is that the BaseUI and TitleBar both have functions called showAutoLogin() and hideAutoLogin(), but they’re functionally different.  We’ll have to be careful to rename those.

Here’s all the functions I added to MainActivity to get this to work:

    private void inflateAutoLoginBar() {
        if (m_autologin != null) {
            return;
        }
        ViewStub stub = (ViewStub) findViewById(R.id.autologin_stub);
        m_autologin = (AutologinBar) stub.inflate();
        m_autologin.setActivity(this);
    }
	
    public void setDeviceAccountLogin(DeviceAccountLogin dal) {
	m_dal = dal;
    }

    public DeviceAccountLogin getDeviceAccountLogin() {
        return m_dal;
    }
	
    /* First pair of showAutoLogin()/hideAutoLogin() taken from Android Browser BaseUI.java
     * They simply call m_autologin.updateAutoLogin()
     */
    public void showAutoLogin()
    {
        updateAutoLogin(false);
    }
	
    public void hideAutoLogin()
    {
        updateAutoLogin(false);
    }
	
    private void updateAutoLogin(boolean animate)
    {
        if(m_autologin == null) {
            if(getDeviceAccountLogin() == null) {
                return;
            }
            inflateAutoLoginBar();
        }
        m_autologin.updateAutoLogin(this, animate);
    }

    /* Second pair of showAutoLogin()/hideAutoLogin() taken from Android Browser TitleBar.java       */
	
    public void hideAutoLogin(boolean animate) {
        if (animate) {
            Animation anim = AnimationUtils.loadAnimation(getBaseContext(),
                    R.anim.autologin_exit);
            anim.setAnimationListener(new AnimationListener() {
                @Override
                public void onAnimationEnd(Animation a) {
                    m_autologin.setVisibility(View.GONE);
                    m_webview.invalidate();
                }

                @Override
                public void onAnimationStart(Animation a) {
                }

                @Override
                public void onAnimationRepeat(Animation a) {
                }
            });
            m_autologin.startAnimation(anim);
        } else /*if (m_autologin.getAnimation() == null)*/ {
            m_autologin.setVisibility(View.GONE);
            m_webview.invalidate();
        }
    }

    public void showAutoLogin(boolean animate) {
        if (m_autologin == null) {
            inflateAutoLoginBar();
        }
        m_autologin.setVisibility(View.VISIBLE);
        if (animate) {
            m_autologin.startAnimation(AnimationUtils.loadAnimation(
                    this, R.anim.autologin_enter));
        }
    }

Git commands:

Removing Un-tracked files:

git clean -f

But beware… there’s no going back. Use -n or --dry-run to preview the damage you’ll do.

If you want to also remove directories, run git clean -f -d

If you just want to remove ignored files, run git clean -f -X

If you want to remove ignored as well as non-ignored files, run git clean -f -x

Note the case difference on the X for the two latter commands.

If clean.requireForce is set to “true” (the default) in your configuration, then unless you specify -f nothing will actually happen, with a recent enough version of git.

Note that as of git-1.6.0, the dashed style of writing git commands (ie, git-clean instead of git clean) is obsoleted.

See the git-clean docs for more information.