I’ve integrated android’s account management in my application and I can manage accounts from the
Accounts & Sync settings.
I want to have the classic login activity that forwards the user to his home activity on successful login, having the option to remember the user’s password. However, the
AccountAuthenticatorActivity must return its result to the
AccountManager with the credentials and the rest of the account information, calling an explicit
finish() and returning the intent.
How can I give the
AccountManager the info it needs without having to
finish() my login activity?
AccountManager is not meant to be called by an interactive application, but rather by a Sync Adapter. (A great tutorial is “Did You Win Yet? » Writing an Android Sync Provider” Part 1 and Part 2 which gives great code examples but doesn’t do as great a job of explaining the data flow.) It’s like this:
You develop a ContentProvider which wraps a database. You build a SyncAdapter (this is a background Service) to connect to a server and fetch data and sync the ContentProvider to match the server. Then, your UI queries to the ContentProvider to show the fetched data. There are some methods to directly query for specific information as well, if you want to search and cache results for example. See Developing RESTful Android Apps for a nice hour-long session on how the data model should look. They give three architecture examples, starting from a “naïve” implementation then progressing to the proper SyncAdapter model.
As for authentication itself, the way SyncAdapter uses the AccountManager is to obtain an authentication token. This is a (often) a big hexidecimal value, that is passed as part of the HTML headers in lieu of a username/password pair. Think of it as a one-session unique key. Posession of the key is proof of authentication, and they expire periodically. When they expire, you reauthenticate and fetch a new one. SyncAdapater asks AccountManager for an auth token for a specific account-type / username combination. AccountManager auths with the server (asking the user for a new password if necessary due to change) and returns the token to the SyncAdapter, which uses it from then on.
If this model isn’t appropriate for your application, you need to manually handle login/logout in your app code instead. Kind of a pain, I know.
@jcwenger That is not entirely correct. You can use the
AccountManager from an interactive application as well. For example, you can add accounts without invoking the account manager interface by using
On the “Did You Win Yet?” article you can clearly see that the account manager is invoked from the application’s own
Activity. This is useful if the application has its own account management interface.
My version of the ‘classic flow’ using AccountManager:
I use my AuthenticatorActivity both for the normal case where it’s used via. Accounts & Sync Settings, but I also open it out for use by applications that rely upon the same Accounts.
I have a separate apk with the Authenticator implemented and other apps (separate apks) that rely upon these Accounts.
the normal case: the user adds/authenticates via. Accounts & Sync (as per the Android sample project)
handle authentication requests from external apps:
How? I provide an intent filter in the Authenticator app’s Manifest so other apps can instantiate the AuthenticatorActivity via. startActivityForResult (they must include an intent extra that indicates who they are (their app’s package)). In the AuthenticatorActivity I detect this case and refrain from calling setAccountAuthenticatorResult when the authentication process has come to a end because I reserved it’s use for the normal case above. The user enters their credentials and presses Sign In: AccountManger is checked for a matching account and if matched I
persist that Account’s username as the active user for the calling app’s package. I then return an intent to the calling app via. setResult indicating success, the username and account type. In the case where the Account didn’t exist I go through the process that the normal case goes through, i.e. calling addAccountExplicitly and then set the active user and then as before calling setResult and finish.
Hope this helps someone.