{"id":1035,"date":"2013-06-19T01:00:00","date_gmt":"2013-06-18T23:00:00","guid":{"rendered":"https:\/\/www.fussylogic.co.uk\/blog\/?p=1035"},"modified":"2013-07-08T23:21:38","modified_gmt":"2013-07-08T22:21:38","slug":"android-authenticators-ii","status":"publish","type":"post","link":"https:\/\/www.fussylogic.co.uk\/blog\/?p=1035","title":{"rendered":"Android Authenticators II"},"content":{"rendered":"<p><a href=\"?p=\">Last time<\/a> we saw how to make Android support our own custom account type. This time we\u00e2\u20ac\u2122ll see how to make use of that account type in our own application.<\/p>\n<p>How does our app call <code>getAuthToken()<\/code>?<\/p>\n<ul>\n<li>Get an account name of the appropriate type (i.e.\u00c2\u00a0our custom type)<\/li>\n<li>If there are no accounts: start an asynchronous account addition in motion.<\/li>\n<li>If there is an account: set an asynchronous authentication token retrieval in motion<\/li>\n<li>Wait for asynchronous response<\/li>\n<\/ul>\n<p>First we\u00e2\u20ac\u2122ll need some permissions \u00e2\u20ac\u201c <code>GET_ACCOUNTS<\/code> for <code>AccountManager.getAccountsByType()<\/code> and <code>USE_CREDENTIALS<\/code> for <code>getAuthToken()<\/code>.<\/p>\n<pre class=\"sourceCode xml\"><code class=\"sourceCode xml\">    <span class=\"kw\">&lt;uses-permission<\/span><span class=\"ot\"> android:name=<\/span><span class=\"st\">&quot;android.permission.GET_ACCOUNTS&quot;<\/span> <span class=\"kw\">\/&gt;<\/span>\n    <span class=\"kw\">&lt;uses-permission<\/span><span class=\"ot\"> android:name=<\/span><span class=\"st\">&quot;android.permission.USE_CREDENTIALS&quot;<\/span> <span class=\"kw\">\/&gt;<\/span><\/code><\/pre>\n<p>Then we\u00e2\u20ac\u2122ll make a new top-level activity as normal, in which we\u00e2\u20ac\u2122ll take a number of steps:<\/p>\n<ul>\n<li>Establish our unique account type (fetch this as a string resource)<\/li>\n<li>Fetch the list of accounts with that type (<code>GET_ACCOUNTS<\/code> permission)<\/li>\n<li>Initiate an authentication token fetch (<code>USE_CREDENTIALS<\/code> permission)<\/li>\n<\/ul>\n<h2 id=\"account-list\">Account List<\/h2>\n<p>In your <code>Activity<\/code>\u00e2\u20ac\u2122s <code>onCreate()<\/code> method, you\u00e2\u20ac\u2122ll do something like this:<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    mAccountType = <span class=\"fu\">getString<\/span>(R.<span class=\"fu\">string<\/span>.<span class=\"fu\">authenticator_account_type<\/span>);\n    mAccountManager = AccountManager.<span class=\"fu\">get<\/span>(<span class=\"kw\">this<\/span>);\n\n    <span class=\"co\">\/\/ TODO: UI to pick account, for now we&#39;ll just take the first<\/span>\n    Account[] acc = mAccountManager.<span class=\"fu\">getAccountsByType<\/span>(mAccountType);\n    <span class=\"kw\">if<\/span>( acc.<span class=\"fu\">length<\/span> == <span class=\"dv\">0<\/span> ) {\n        Log.<span class=\"fu\">e<\/span>(<span class=\"kw\">null<\/span>, <span class=\"st\">&quot;No accounts of type &quot;<\/span> + mAccountType + <span class=\"st\">&quot; found&quot;<\/span>);\n        <span class=\"co\">\/\/ TODO: add account<\/span>\n        <span class=\"kw\">return<\/span>;\n    }\n    mAccount = acc[<span class=\"dv\">0<\/span>];\n    <span class=\"fu\">startAuthTokenFetch<\/span>();<\/code><\/pre>\n<p>The error handling is poor here, you should report errors to the user, not just exit; but that would complicate my example, so I haven\u00e2\u20ac\u2122t done it. I\u00e2\u20ac\u2122ve also not added support for multiple instances of the same account type \u00e2\u20ac\u201c instead opting to just use the first one in the returned list for now.<\/p>\n<h2 id=\"fetch-an-authentication-token\">Fetch an Authentication Token<\/h2>\n<p>The <a href=\"http:\/\/developer.android.com\/reference\/android\/accounts\/AccountManager.html#getAuthToken(android.accounts.Account,%20java.lang.String,%20android.os.Bundle,%20android.app.Activity,%20android.accounts.AccountManagerCallback%3Candroid.os.Bundle%3E,%20android.os.Handler)\"><code>getAuthToken()<\/code><\/a> call is asynchronous, so we don\u00e2\u20ac\u2122t get a token back, we start a process that happens in the background, and we supply callbacks to alert us when that process has completed (or failed):<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    <span class=\"kw\">private<\/span> <span class=\"dt\">void<\/span> <span class=\"fu\">startAuthTokenFetch<\/span>() {\n        Bundle options = <span class=\"kw\">new<\/span> <span class=\"fu\">Bundle<\/span>();\n        mAccountManager.<span class=\"fu\">getAuthToken<\/span>(\n                mAccount,\n                mAuthTokenType,\n                options,\n                <span class=\"kw\">this<\/span>,\n                <span class=\"kw\">new<\/span> <span class=\"fu\">OnAccountManagerComplete<\/span>(),\n                <span class=\"kw\">new<\/span> Handler(<span class=\"kw\">new<\/span> <span class=\"fu\">OnError<\/span>())\n            );\n    }<\/code><\/pre>\n<p><code>mAccount<\/code> we looked up above; <code>mAuthTokenType<\/code> is entirely up to you and your application. If you have different levels of access to an API, then you could handle that by passing a name for each of those types to the authenticator <code>Service<\/code> via this parameter \u00e2\u20ac\u201c your service then handles obtaining an appropriate token for that type however you wish, so really this is an entirely opaque parameter to Android other than to use it as a lookup key for the various tokens you store.<\/p>\n<p>Then we just provide the <code>OnAccountManagerComplete()<\/code> callback class, and we\u00e2\u20ac\u2122re done.<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    <span class=\"kw\">private<\/span> <span class=\"kw\">class<\/span> OnAccountManagerComplete <span class=\"kw\">implements<\/span> AccountManagerCallback&lt;Bundle&gt; {\n        <span class=\"fu\">@Override<\/span>\n        <span class=\"kw\">public<\/span> <span class=\"dt\">void<\/span> <span class=\"fu\">run<\/span>(AccountManagerFuture&lt;Bundle&gt; result) {\n            Bundle bundle;\n            <span class=\"kw\">try<\/span> {\n                bundle = result.<span class=\"fu\">getResult<\/span>();\n            } <span class=\"kw\">catch<\/span> (OperationCanceledException e) {\n                e.<span class=\"fu\">printStackTrace<\/span>();\n                <span class=\"kw\">return<\/span>;\n            } <span class=\"kw\">catch<\/span> (AuthenticatorException e) {\n                e.<span class=\"fu\">printStackTrace<\/span>();\n                <span class=\"kw\">return<\/span>;\n            } <span class=\"kw\">catch<\/span> (IOException e) {\n                e.<span class=\"fu\">printStackTrace<\/span>();\n                <span class=\"kw\">return<\/span>;\n            }\n            mAuthToken = bundle.<span class=\"fu\">getString<\/span>(AccountManager.<span class=\"fu\">KEY_AUTHTOKEN<\/span>);\n            Log.<span class=\"fu\">d<\/span>(<span class=\"st\">&quot;main&quot;<\/span>, <span class=\"st\">&quot;Received authentication token &quot;<\/span> + mAuthToken);\n        }\n    }<\/code><\/pre>\n<p>What you do with that token is then up to you and your application-specific needs; for example you might include it in a JSON command to your server RPC interface.<\/p>\n<h2 id=\"handling-multiple-accounts\">Handling Multiple Accounts<\/h2>\n<p>Above, we didn\u00e2\u20ac\u2122t cope very well if there was more than one account type defined. Let\u00e2\u20ac\u2122s do better.<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    Account[] acc = mAccountManager.<span class=\"fu\">getAccountsByType<\/span>(mAccountType);\n    <span class=\"kw\">if<\/span>( acc.<span class=\"fu\">length<\/span> == <span class=\"dv\">0<\/span> ) {\n        Log.<span class=\"fu\">e<\/span>(<span class=\"kw\">null<\/span>, <span class=\"st\">&quot;No accounts of type &quot;<\/span> + mAccountType + <span class=\"st\">&quot; found&quot;<\/span>);\n        <span class=\"kw\">return<\/span>;\n    } <span class=\"kw\">else<\/span> {\n        Log.<span class=\"fu\">i<\/span>(<span class=\"st\">&quot;main&quot;<\/span>, <span class=\"st\">&quot;Found &quot;<\/span> + acc.<span class=\"fu\">length<\/span> + <span class=\"st\">&quot; accounts of type &quot;<\/span> + mAccountType);\n\n        Intent intent = AccountManager.<span class=\"fu\">newChooseAccountIntent<\/span>(\n                <span class=\"kw\">null<\/span>,\n                <span class=\"kw\">null<\/span>,\n                <span class=\"kw\">new<\/span> String[]{<span class=\"fu\">getString<\/span>(R.<span class=\"fu\">string<\/span>.<span class=\"fu\">authenticator_account_type<\/span>)},\n                <span class=\"kw\">false<\/span>,\n                <span class=\"kw\">null<\/span>,\n                <span class=\"fu\">getString<\/span>(R.<span class=\"fu\">string<\/span>.<span class=\"fu\">auth_token_type_default<\/span>),\n                <span class=\"kw\">null<\/span>,\n                <span class=\"kw\">null<\/span>);\n        <span class=\"fu\">startActivityForResult<\/span>(intent, ACCOUNT_CHOOSER_ACTIVITY);\n    }<\/code><\/pre>\n<p>We\u00e2\u20ac\u2122re using <code>startActivityForResult()<\/code>, so we\u00e2\u20ac\u2122ve defined a private code to identify this remote call, <code>ACCOUNT_CHOOSER_ACTIVITY<\/code>, and we\u00e2\u20ac\u2122ll have to implement <code>onActivityResult()<\/code> to catch the answer when it comes back.<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    <span class=\"fu\">@Override<\/span>\n    <span class=\"kw\">protected<\/span> <span class=\"dt\">void<\/span> <span class=\"fu\">onActivityResult<\/span>(<span class=\"dt\">int<\/span> requestCode, <span class=\"dt\">int<\/span> resultCode, Intent data) {\n        <span class=\"kw\">if<\/span>( resultCode == RESULT_CANCELED)\n            <span class=\"kw\">return<\/span>;\n        <span class=\"kw\">if<\/span>( requestCode == ACCOUNT_CHOOSER_ACTIVITY ) {\n            Bundle bundle = data.<span class=\"fu\">getExtras<\/span>();\n            mAccount = <span class=\"kw\">new<\/span> <span class=\"fu\">Account<\/span>(\n                    bundle.<span class=\"fu\">getString<\/span>(AccountManager.<span class=\"fu\">KEY_ACCOUNT_NAME<\/span>),\n                    bundle.<span class=\"fu\">getString<\/span>(AccountManager.<span class=\"fu\">KEY_ACCOUNT_TYPE<\/span>)\n                    );\n            Log.<span class=\"fu\">d<\/span>(<span class=\"st\">&quot;main&quot;<\/span>, <span class=\"st\">&quot;Selected account &quot;<\/span> + mAccount.<span class=\"fu\">name<\/span> + <span class=\"st\">&quot;, fetching&quot;<\/span>);\n            <span class=\"fu\">startAuthTokenFetch<\/span>();\n        }\n    }<\/code><\/pre>\n<p>If there is one account only, Android is sensible enough to just return that account, if there are multiple accounts, you\u00e2\u20ac\u2122ll get a popup list from which you can pick, and then the result returned. We\u00e2\u20ac\u2122ve just constructed an <code>Account<\/code> object for our <code>mAccount<\/code> member with the returned value, and continued as previously with fetching the token. In the end though, we\u00e2\u20ac\u2122re still just calling <code>startAuthTokenFetch()<\/code> with an account.<\/p>\n<h2 id=\"no-accounts-configured\">No Accounts Configured<\/h2>\n<p>We\u00e2\u20ac\u2122ve now dealt with multiple accounts being registered, but what if there are none? Rather than give the user instructions that your application only works if they go to <code>Settings-&gt;Add Account<\/code>, why don\u00e2\u20ac\u2122t we just forward them there? Let\u00e2\u20ac\u2122s rewrite our \u00e2\u20ac\u0153no account\u00e2\u20ac\u009d code:<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    <span class=\"kw\">if<\/span>( acc.<span class=\"fu\">length<\/span> == <span class=\"dv\">0<\/span> ) {\n        Log.<span class=\"fu\">d<\/span>(<span class=\"st\">&quot;main&quot;<\/span>, <span class=\"st\">&quot;No suitable account found, directing user to add one&quot;<\/span>);\n        <span class=\"co\">\/\/ No account, push the user into adding one.  We use addAccount<\/span>\n        <span class=\"co\">\/\/ rather than an Intent so that we can specify our own account<\/span>\n        <span class=\"co\">\/\/ type -- requires MANAGE_ACCOUNTS permission<\/span>\n        mAccountManager.<span class=\"fu\">addAccount<\/span>(\n                <span class=\"fu\">getString<\/span>(R.<span class=\"fu\">string<\/span>.<span class=\"fu\">authenticator_account_type<\/span>),\n                <span class=\"fu\">getString<\/span>(R.<span class=\"fu\">string<\/span>.<span class=\"fu\">auth_token_type_default<\/span>),\n                <span class=\"kw\">null<\/span>,\n                <span class=\"kw\">new<\/span> <span class=\"fu\">Bundle<\/span>(),\n                <span class=\"kw\">this<\/span>,\n                <span class=\"kw\">new<\/span> <span class=\"fu\">OnAccountAddComplete<\/span>(),\n                <span class=\"kw\">null<\/span>);\n    } <span class=\"kw\">else<\/span> {\n        <span class=\"co\">\/\/ ... we covered this above ...<\/span>\n    }<\/code><\/pre>\n<p>Just as with the <code>getAuthToken()<\/code> call, <code>AccountManager<\/code> operations are always asynchronous, so we have to finish with the callback.<\/p>\n<pre class=\"sourceCode java\"><code class=\"sourceCode java\">    <span class=\"kw\">private<\/span> <span class=\"kw\">class<\/span> OnAccountAddComplete <span class=\"kw\">implements<\/span> AccountManagerCallback&lt;Bundle&gt; {\n        <span class=\"fu\">@Override<\/span>\n        <span class=\"kw\">public<\/span> <span class=\"dt\">void<\/span> <span class=\"fu\">run<\/span>(AccountManagerFuture&lt;Bundle&gt; result) {\n            Bundle bundle;\n            <span class=\"kw\">try<\/span> {\n                bundle = result.<span class=\"fu\">getResult<\/span>();\n            } <span class=\"kw\">catch<\/span> (OperationCanceledException e) {\n                e.<span class=\"fu\">printStackTrace<\/span>();\n                <span class=\"kw\">return<\/span>;\n            } <span class=\"kw\">catch<\/span> (AuthenticatorException e) {\n                e.<span class=\"fu\">printStackTrace<\/span>();\n                <span class=\"kw\">return<\/span>;\n            } <span class=\"kw\">catch<\/span> (IOException e) {\n                e.<span class=\"fu\">printStackTrace<\/span>();\n                <span class=\"kw\">return<\/span>;\n            }\n            mAccount = <span class=\"kw\">new<\/span> <span class=\"fu\">Account<\/span>(\n                    bundle.<span class=\"fu\">getString<\/span>(AccountManager.<span class=\"fu\">KEY_ACCOUNT_NAME<\/span>),\n                    bundle.<span class=\"fu\">getString<\/span>(AccountManager.<span class=\"fu\">KEY_ACCOUNT_TYPE<\/span>)\n                    );\n            Log.<span class=\"fu\">d<\/span>(<span class=\"st\">&quot;main&quot;<\/span>, <span class=\"st\">&quot;Added account &quot;<\/span> + mAccount.<span class=\"fu\">name<\/span> + <span class=\"st\">&quot;, fetching&quot;<\/span>);\n            <span class=\"fu\">startAuthTokenFetch<\/span>();\n        }\n    }<\/code><\/pre>\n<p>Done. If there is no account defined when our <code>Activity<\/code> starts, <code>AccountManager<\/code> is triggered to create one. We saw last time how that actually ends up back in our application by our provision of an authenticator <code>Service<\/code>; that service tells Android\u00e2\u20ac\u2122s account manager to run our <code>LoginActivity<\/code>, which then returns to the account manager, which then calls this <code>OnAccountAddComplete<\/code>, which then runs the same end point: <code>startAuthTokenFetch()<\/code>. All roads lead to Rome.<\/p>\n<p>Next time I\u00e2\u20ac\u2122ll be looking at how we tap in to Android\u00e2\u20ac\u2122s synchronisation infrastructure to keep our app up-to-date with a remote server.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Last time we saw how to make Android support our own custom account type. This time we\u00e2\u20ac\u2122ll see how to make use of that account type in our own application. How does our app call getAuthToken()? Get an account name of the appropriate type (i.e.\u00c2\u00a0our custom type) If there are no accounts: start an asynchronous\u2026 <span class=\"read-more\"><a href=\"https:\/\/www.fussylogic.co.uk\/blog\/?p=1035\">Read More &raquo;<\/a><\/span><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[9,70,69,42,6],"_links":{"self":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1035"}],"collection":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1035"}],"version-history":[{"count":8,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1035\/revisions"}],"predecessor-version":[{"id":1129,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=\/wp\/v2\/posts\/1035\/revisions\/1129"}],"wp:attachment":[{"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1035"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1035"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.fussylogic.co.uk\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1035"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}