Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement AccountManager in my app

I try to implement an account manager in my app to avoid that the user has to log in each time he opens the app.

So basically, I already have my Authentication Activity where the user can put its login and password and where we receive a token from the server (the authentication is basic for now). Now I want to add the AccountManager but I don't really understand which part would go where.

What I need is pretty basic:

  • add an account if I never logged in before
  • log automatically if my account exists
  • if the auto authentication doesn't work get a new token on the server

Here is my code :

AuthenticationActivity.java

public class AuthenticationActivity extends Activity {

    private EditText editTextUsername;
    private EditText editTextPassword;
    private Button buttonLogin;
    private ProgressBar spinner;
    private TextView error;
    private TextView register;

    private boolean accountRegistred;

    AccountManager accountManager;

    public static final String AUTHENTICATION = "authentication"; //action

    private ConnectionSuccessReceiver connectionSuccessReceiver = new ConnectionSuccessReceiver();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.authentification);

        accountManager = AccountManager.get(this);
        Account[] accounts = accountManager.getAccountsByType("login");

        if (accounts.length > 0) {
            //If there is an account
        } else {
            accountRegistred = false;
            editTextUsername = (EditText) findViewById(R.id.editText_login);
            editTextUsername.setVisibility(View.VISIBLE);
            editTextPassword = (EditText) findViewById(R.id.editText_password);
            editTextPassword.setVisibility(View.VISIBLE);
            buttonLogin = (Button) findViewById(R.id.button_connection);
            buttonLogin.setVisibility(View.VISIBLE);
            error = (TextView) findViewById(R.id.textView_error);
            register = (TextView) findViewById(R.id.textView_register);
            register.setVisibility(View.VISIBLE);
            spinner = (ProgressBar) findViewById(R.id.progressBar);

            buttonLogin.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //Here we start the service which will reach the server 
                    Intent i = new Intent(getApplicationContext(), AuthenticationService.class);
                    i.putExtra("username", editTextUsername.getText().toString());
                    i.putExtra("password", editTextPassword.getText().toString());
                    getApplication().startService(i);
                    spinner.setVisibility(View.VISIBLE);
                    error.setVisibility(View.INVISIBLE);
                }
            });

            register.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    startActivity(new Intent(AuthenticationActivity.this, RegisterActivity.class));
                }
            });
        }
        registerReceiver(connectionSuccessReceiver, new IntentFilter(AUTHENTICATION));
    }

    private class ConnectionSuccessReceiver extends BroadcastReceiver {
        //Called when the server returns success after authentication, we get the TOKEN here
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getStringExtra("STATE").equals("CONNECTED")) {
                Intent i = new Intent(AuthenticationActivity.this, MainActivity.class);
                i.putExtra("TOKEN", intent.getStringExtra("TOKEN"));
                startActivity(i);
            } else {
                spinner.setVisibility(View.INVISIBLE);
                error.setVisibility(View.VISIBLE);
            }
            finish();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(connectionSuccessReceiver);
    }
}

AuthenticatorService.java

public class AuthenticatorService extends Service {

    /**
     * The implementation of the class |AccountAuthenticatorImpl|.
     * It is implemented as a singleton
     */
    private static AccountAuthenticator accountAuthenticator = null;

    /**
     * The main constructor.
     */
    public AuthenticatorService() {
        super();
    }

    /**
     * The bind method of the service.
     * @param intent The intent used to invoke the service
     * @return The binder of the class which has implemented |AbstractAccountAuthenticator|
     */
    @Override
    public IBinder onBind(Intent intent) {
        IBinder ret = null;
        if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
            ret = getAuthenticator().getIBinder();
        }
        return ret;
    }

    /**
     * The method used to obtain the authenticator. It is implemented as a singleton
     * @return The implementation of the class |AbstractAccountAuthenticator|
     */
    private AccountAuthenticator getAuthenticator() {
        if (AuthenticatorService.accountAuthenticator == null) {
            AuthenticatorService.accountAuthenticator = new AccountAuthenticator(this);
        }

        return AuthenticatorService.accountAuthenticator;
    }

    public class AccountAuthenticator extends AbstractAccountAuthenticator {
        private Context context;

        public AccountAuthenticator(Context context) {
            super(context);
            this.context = context;
        }

        @Override
        public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse, String s) {
            return null;
        }

        @Override
        public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {

            Bundle reply = new Bundle();

            Intent i = new Intent(context, AuthenticationActivity.class);
            i.setAction("com.readyo.app.authentication.addnewaccount");
            i.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
            i.putExtra("AuthTokenType", authTokenType);

            reply.putParcelable(AccountManager.KEY_INTENT, i);

            return reply;
        }

        @Override
        public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, Bundle bundle) throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException {
            return null;
        }

        @Override
        public String getAuthTokenLabel(String s) {
            return null;
        }

        @Override
        public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException {
            return null;
        }

        @Override
        public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String[] strings) throws NetworkErrorException {
            return null;
        }
    }
}

I have also code to reach the server via HTTP but I'm not sure it would be important here.

Thank you for your time.

like image 587
ilansas Avatar asked Jan 13 '15 16:01

ilansas


1 Answers

It's a bit late response but maybe this sample could help you: https://github.com/dawidgdanski/AccountAuthenticatorExample

I created it some time ago but the logic with signing up/logging in may be helpful

add an account if I never logged in before:
  1. If your app flow requires user to log in order to gain access to the data, then simply declare your LoginActivity as the primary one to be displayed.

Once you validate and verify user credentials, call AccountManager.addAccountExcplicitly() method.

  1. On the other hand, if you expose some screens to be seen for anonymous users then in the application part (settings or whatever) where you provide login/sign up functionality call AccountManager.addAccount(). This call activates your AccountAuthenticator that processes your request in YourAccountAuthenticator.addAccount() and may display LoginActivity/SignUpActivity according to your needs.

Please, bear in mind that you may create app-specific account from System Settings as well.

    log automatically if my account exists

Well, I'm not sure whether I understand your demand correctly. Once you store Account in AccountManager's meta data it is available once you call AccountManager.getAccountsByType("my.account.type"). If you want to log in automatically then you must somewhere store your credentials which is obviously under the threat of sensitive data leak.

 if the auto authentication doesn't work get a new token on the server

There is an AccountManager.invalidateAuthToken() method that removes currently stored authToken and calls for another one.

You can launch the example app, I think it may solve at least some of your problems because it covers the following logic:

  • login/signup
  • auth token invalidation
  • displaying currently logged accounts
  • logout

Cheers

like image 168
dawid gdanski Avatar answered Nov 15 '22 01:11

dawid gdanski