Android

26 out, 2017

App Android com Firebase – Autenticação

Publicidade

Lembro quando comecei a trabalhar com a plataforma Android, em 2012 e me deparei com todo tipo de desafio inédito para mim, sendo o principal deles a necessidade de ficar construindo todo o tipo de APIs usando tecnologias web para serem consumidas pelo seu app. Preciso acessar o banco de dados remoto? Cria API. Preciso de autenticação? Cria API. Preciso de algum processamento mais pesado? Cria API.

Tem algum tempo que isso mudou. Com a aquisição da startup Firebase, o Google disponibilizou uma plataforma de backend-as-a-service pronta para uso em todo tipo de aplicação. E a ideia deste artigo completo, dividido em etapas, é criarmos um app Android que use as APIs fornecidas pelo Firebase para lhe ajudar a entender todo o poder desta plataforma.

Começaremos com a autenticação, mas passaremos por cada uma das grandes frentes do Firebase.

Em outros artigos, devo cobrir as demais APIs do Firebase, como: Database, Storage, Analytics, Crash, Messaging e Ads.

Atenção: se você nunca programou para Android antes, sugiro fazer primeiro este tutorial aqui e/ou ler meu livro de Android.

Vamos lá!

1 – Introdução ao Firebase

Basicamente o Google Firebase é uma plataforma de Backend-as-a-Service, fornecendo um set de recursos prontos para usar em APIs e SDKs que permitem ao desenvolver Android se focar na construção do seu app, sem se preocupar com desenvolvimento de backend, banco de dados, etc. Com um modelo de negócio escalável, ele permite que você comece seu app sem pagar nada e depois ir pagando conforme a sua base de usuários cresce.

Pré-requisitos

Para poder usar em apps Android, é necessário que o dispositivo possua a versão 4.0 ou superior, e o Google Play Services 11.0.4 ou superior. Também é necessário que você tenha instalado na sua máquina o Google Play Services do Repositório do Google, disponível no SDK Manager do Android Studio. E sim, é necessário usar o Android Studio versão 1.5 ou superior.

Existem duas formas de adicionar Firebase ao seu projeto Android no Android Studio. A primeira é inserindo as dependências manualmente no arquivo build.gradle. E a outra, mais moderna e disponível no Android Studio a partir da versão 2.2, é usando o Firebase Assistant, que é a que usarei neste tutorial.

Criando o projeto

Nós vamos criar um app de força de vendas. Trabalhei com isto em 2007, no meu primeiro emprego oficialmente como programador (antes disso trabalhava com suporte, mas também programava). Basicamente imagine que representantes de vendas que trabalham na rua possam abandonar suas agendas físicas e usar esse app para registrar as vendas realizadas, os pedidos solicitados e que possam tirar relatórios simples de pedidos, vendas e dívidas. Nesse momento de desemprego no país em que muita gente está trabalhando por conta, principalmente como representantes autônomos (Hinode, Mary Kay, Jequiti, etc), um app desses pode ajudar muita gente.

Crie no Android Studio um projeto chamado ForcaVendas, com SDK mínimo Android 4.0.3 (API 15) e uma Basic Activity por padrão (MainActivity). A partir de agora criaremos todo o app de força de vendas usando cada um dos recursos fornecidos na plataforma Firebase.

Atenção!

Uma última dica antes de partir para o código: se for usar o emulador nativo do Android para os testes, certifique-se de criar seu AVD usando uma imagem que possua a Play Store instalada (a imagem abaixo mostra o ícone da Play Store em algumas imagens), para que o Google Play Services possa ser atualizado facilmente (uma das exigências do Firebase é ter a última versão do Google Play Services instalado).

Imagens com Play Store

2 – Firebase Authentication

Nosso app exigirá autenticação do vendedor. Isso porque, como nossos dados serão armazenados na nuvem do Google, temos que saber quem está autenticando no app para carregar os dados das vendas e prospecções deste usuário específico.

Adicione uma nova Activity do tipo Login Activity no seu projeto, como abaixo:

Login Activity

Depois disso, vá no menu Tools > Firebase e o Firebase Assistant abrirá, com diversas opções. Vamos começar usando os recursos de autenticação do Firebase clicando na opção Authentication > Email and password authentication.

O Firebase Assistant abrirá um passo-a-passo para configurar o uso desse recurso no seu projeto. O primeiro passo é conectar seu app ao Firebase, que você faz clicando no botão “Connect to Firebase”.

Passo 1: Connect to Firebase

Ao clicar no botão, você será levado ao site do Google Accounts para fornecer permissões ao Firebase na sua conta do Google. Se tudo aconteceu corretamente, você deve ver a tela de sucesso informando que você já pode voltar ao Android Studio.

Firebase conectado

Quando voltar ao Android Studio, você terá de configurar um novo Firebase Project, que basicamente é o seu projeto de backend. Use o mesmo nome do seu projeto (ForcaVendas) e selecione a região Brazil, clicando no botão “Connect to Firebase” para continuar, como abaixo.

Configuração do Firebase

Nesse momento o Android Studio baixará algumas dependências necessárias e adicionará as configurações das mesmas no seu build.gradle. Tudo isso sem que você tenha de saber muitos detalhes do processo. Quando esse processo automático terminar, o Firebase Assistant deve exibir que o Firebase está conectado e você deve passar para o passo 2, “Add Firebase Authentication to your app”.

Add Firebase Authentication to your app

Ao clicar no botão, o Android Studio lhe apresentará algumas modificações que serão feitas automaticamente para você confirmar. Confirme e aguarde que as alterações se completem para ver que o passo 2 informará que as dependências foram instaladas corretamente.

Dependências corretamente instaladas

Note que há uma descrição adicional logo abaixo do passo 2, que informa que você deve habilitar no Firebase Console o Sign-In Method que será utilizado no seu app.

Sendo assim, acesse o Firebase Console, que é o software web usado para gerenciar seus projetos Firebase. No nosso caso, basta clicar no projeto ForcaVendas, como na imagem abaixo:

Firebase Console

No menu lateral, selecione a opção Authentication e depois clique no botão “Configurar Método de Login”.

Método de Login

Escolha a opção Email & Senha e clique em ativar, salvando logo em seguida. Existem diversas outras opções, como usar Facebook Auth ou Google Auth, por exemplo. Aqui usaremos o bom e velho “email e senha”.

A partir do passo 3, o processo é manual, pois o Firebase Assistant apenas ensina como utilizar o recurso de autenticação no seu app, então vamos programar!

3 – Forçar a autenticação

Quando nosso app foi criado, definimos que a MainActivity seria a activity inicial, lembra?

Pois é, mas caso o usuário não esteja autenticado em nosso app, ele não deve ver a MainActivity primeiro, mas sim a LoginActivity. Então, vamos começar a programar nossa autenticação verificando na MainActivity se o usuário está logado. Se ele não estiver, vamos redirecioná-lo para a LoginActivity.

Para começar, na sua MainActivity.java coloque a seguinte variável a nível de classe (imports serão necessários, use o recurso ALT+ENTER do Android Studio para auto completar as dependências):

private FirebaseAuth mAuth;

E agora, dentro do seu onCreate da MainActivity, coloque o seguinte código que verifica se há um usuário logado no app e, caso contrário, redireciona o fluxo para a LoginActivity:

mAuth = FirebaseAuth.getInstance();
FirebaseUser user = mAuth.getCurrentUser();
if (user != null) {
    Toast.makeText(getApplicationContext(), "Bem vindo de volta " + user.getEmail() + "!", Toast.LENGTH_LONG).show();
} else {
    Intent intent = new Intent(this, LoginActivity.class);
    startActivity(intent);
    finish();
}

Neste código nós carregamos a instância atual da autenticação do Firebase (FirebaseAuth), e com ela tentamos carregar o usuário atualmente autenticado. Se ele estiver null, é porque ninguém está autenticado no app deste dispositivo.

Com isso, você vai notar que, ao mandar rodar o app, mesmo a MainActivity sendo a principal, você deverá ver primeiro a LoginActivity, pois não está autenticado. Na verdade nem mesmo você possui um usuário, o que faremos no passo seguinte.

Tela de Login

4 – Permitir o registro

A própria LoginActivity já considera a possibilidade do auto-registro por parte do usuário que ainda não está cadastrado no app. Dependendo das suas necessidades de segurança (e de cadastro), você pode querer fazer algo diferente do que vou fazer aqui, mas a ideia é basicamente o que vou mostrar.

Quando criamos uma Activity com o padrão LoginActivity, o Android Studio não cria apenas o layout XML de uma tela de login, mas uma lógica básica de autenticação na LoginActivity.java, que inclui validação de campos, autenticação via coleção in-memory e algumas coisas mais. Vamos modificar algumas coisas presentes neste exemplo para que fique mais funcional.

Primeiro, abra o activity_login.xml na pasta res/layout para mudarmos o texto do botão de Sign In, sua cor de fundo e para adicionarmos um segundo botão para Sign Up (registro ou cadastro), ficando da seguinte forma:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_horizontal"
    android:orientation="vertical"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="br.com.luiztools.forcavendas.LoginActivity">

    <!-- Login progress -->
    <ProgressBar
        android:id="@+id/login_progress"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="8dp"
        android:visibility="gone" />

    <ScrollView
        android:id="@+id/login_form"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:id="@+id/email_login_form"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <android.support.design.widget.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <AutoCompleteTextView
                    android:id="@+id/email"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/prompt_email"
                    android:inputType="textEmailAddress"
                    android:maxLines="1"
                    android:singleLine="true" />

            </android.support.design.widget.TextInputLayout>

            <android.support.design.widget.TextInputLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">

                <EditText
                    android:id="@+id/password"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="Senha"
                    android:imeActionId="@+id/login"
                    android:imeActionLabel="@string/action_sign_in_short"
                    android:imeOptions="actionUnspecified"
                    android:inputType="textPassword"
                    android:maxLines="1"
                    android:singleLine="true" />

            </android.support.design.widget.TextInputLayout>

            <Button
                android:id="@+id/email_sign_in_button"
                style="?android:textAppearanceSmall"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:background="@color/colorPrimaryDark"
                android:textColor="@android:color/white"
                android:text="Entrar"
                android:textStyle="bold" />

            <Button
                android:id="@+id/email_sign_up_button"
                style="?android:textAppearanceSmall"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="16dp"
                android:text="Registrar"
                android:textStyle="bold" />

        </LinearLayout>
    </ScrollView>
</LinearLayout>

Vamos começar nossa programação declarando a variável abaixo a nível de classe na LoginActivity.java:

private FirebaseAuth mAuth;

Para aproveitar o código já existente, vamos modificar o método attemptLogin para que ele sirva para signin ou signup, de acordo com um parâmetro passado à ele.

private void attemptLoginOrRegister(boolean isNewUser) {
        if (mAuthTask != null) {
            return;
        }

        // Reset errors.
        mEmailView.setError(null);
        mPasswordView.setError(null);

        // Store values at the time of the login attempt.
        String email = mEmailView.getText().toString();
        String password = mPasswordView.getText().toString();

        boolean cancel = false;
        View focusView = null;

        // Check for a valid password, if the user entered one.
        if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
            mPasswordView.setError(getString(R.string.error_invalid_password));
            focusView = mPasswordView;
            cancel = true;
        }

        // Check for a valid email address.
        if (TextUtils.isEmpty(email)) {
            mEmailView.setError(getString(R.string.error_field_required));
            focusView = mEmailView;
            cancel = true;
        } else if (!isEmailValid(email)) {
            mEmailView.setError(getString(R.string.error_invalid_email));
            focusView = mEmailView;
            cancel = true;
        }

        if (cancel) {
            // There was an error; don't attempt login and focus the first
            // form field with an error.
            focusView.requestFocus();
        } else {
            // Show a progress spinner, and kick off a background task to
            // perform the user login attempt.
            showProgress(true);
            if(isNewUser) {
                mAuth.createUserWithEmailAndPassword(email, password).addOnCompleteListener(LoginActivity.this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        showProgress(false);
                        Toast.makeText(getApplicationContext(), "Usuário cadastrado com sucesso. Agora você pode se autenticar com suas credenciais!", Toast.LENGTH_LONG).show();
                    }
                });
            }
            else {
                mAuthTask = new UserLoginTask(email, password);
                mAuthTask.execute((Void) null);
            }
        }
    }

Note que apenas alterei a assinatura do método e criei um if que verifica se é um novo usuário ou não, que está tentando se autenticar/registrar. Se for um novo cadastro (o usuário clicou no botão Registrar), nos comunicamos com o FirebaseAuth para criar um usuário com e-mail e senha informados (createUserWithEmailAndPassword). Quando essa criação terminar (o que depende de Internet sempre pode demorar um pouco), escondemos o loader e exibimos uma mensagem de texto ao usuário informando que agora ele pode se autenticar com o usuário e senha criados.

Com essas mudanças no método, incluindo sua assinatura, vamos mudar no onCreate da LoginActivity as chamadas ao antigo attemptLogin e também vamos adicionar o listener de onClick ao botão de signup:

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

        // Set up the login form.
        mEmailView = (AutoCompleteTextView) findViewById(R.id.email);
        populateAutoComplete();

        mPasswordView = (EditText) findViewById(R.id.password);
        mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
                if (id == R.id.login || id == EditorInfo.IME_NULL) {
                    attemptLoginOrRegister(false);
                    return true;
                }
                return false;
            }
        });

        mAuth = FirebaseAuth.getInstance();
        
        Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
        mEmailSignInButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                attemptLoginOrRegister(false);
            }
        });

        Button mSignUpButton = (Button) findViewById(R.id.email_sign_up_button);
        mSignUpButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                attemptLoginOrRegister(true);
            }
        });

        mLoginFormView = findViewById(R.id.login_form);
        mProgressView = findViewById(R.id.login_progress);
    }

Note que aproveitei para instanciar a variável mAuth com a instância atual de autenticação do Firebase. Note também que nenhum dos dois botões de fato faz alguma coisa, mas sim, delega ao attemptLoginOrRegister que tem o ‘if’ que expliquei anteriormente.

Agora mande rodar sua aplicação, preencha os dois campos e clique no segundo botão, o de registrar.

Nova tela de Login

Você deve ver o loader aparecer por alguns segundos e sumir, voltando à tela de login com uma mensagem de texto (Toast) dizendo que você já pode se autenticar. Não vai funcionar o login, porque não programamos ele ainda (o que faremos no próximo passo), mas antes de falar disso, vamos ver como sabemos os usuários da nossa aplicação.

Volte ao Firebase Console. Na aba Authentication você deve ver os usuários já cadastrados e inclusive poderá realizar cadastro manual se quiser.

Usuários registrados

Uma dica legal é enviar por email ao usuário, um email de boas vindas, falando das features do seu app e informando a senha que ele escolheu. Você pode aprender como enviar emails em Android neste tutorial aqui.

5 – Realizando o login

Agora vamos programar o login!

Para fazer isso, basta modificarmos o ‘if’ que deixamos pronto no método attemptLoginOrRegister anteriormente.

private void attemptLoginOrRegister(boolean isNewUser) {
        if (mAuthTask != null) {
            return;
        }

        // Reset errors.
        mEmailView.setError(null);
        mPasswordView.setError(null);

        // Store values at the time of the login attempt.
        String email = mEmailView.getText().toString();
        String password = mPasswordView.getText().toString();

        boolean cancel = false;
        View focusView = null;

        // Check for a valid password, if the user entered one.
        if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
            mPasswordView.setError(getString(R.string.error_invalid_password));
            focusView = mPasswordView;
            cancel = true;
        }

        // Check for a valid email address.
        if (TextUtils.isEmpty(email)) {
            mEmailView.setError(getString(R.string.error_field_required));
            focusView = mEmailView;
            cancel = true;
        } else if (!isEmailValid(email)) {
            mEmailView.setError(getString(R.string.error_invalid_email));
            focusView = mEmailView;
            cancel = true;
        }

        if (cancel) {
            // There was an error; don't attempt login and focus the first
            // form field with an error.
            focusView.requestFocus();
        } else {
            // Show a progress spinner, and kick off a background task to
            // perform the user login attempt.
            showProgress(true);
            if(isNewUser) {
                mAuth.createUserWithEmailAndPassword(email, password).addOnCompleteListener(LoginActivity.this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        showProgress(false);
                        Toast.makeText(getApplicationContext(), "Usuário cadastrado com sucesso. Agora você pode se autenticar com suas credenciais!", Toast.LENGTH_LONG).show();
                    }
                });
            }
            else {
                mAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(LoginActivity.this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        showProgress(false);
                        if(task.getResult().getUser() != null) {
                            startActivity(new Intent(LoginActivity.this, MainActivity.class));
                            finish();
                        } else{
                            Toast.makeText(getApplicationContext(), "Email e/ou senha incorretos.", Toast.LENGTH_LONG).show();
                        }
                    }
                });
            }
        }
    }

Note que a alteração foi somente no ‘else’, onde uso o método assíncrono signInWithEmailAndPassword que vai no servidor do Firebase, verifica a autenticidade das informações fornecidas e retorna um usuário, se existir, que tenha aquele email e senha. Neste caso, enviamos o usuário para a MainActivity. Caso contrário, exibimos uma mensagem de erro pra ele.

Rode novamente seu projeto e, se tudo deu certo, você deve conseguir se autenticar com um usuário criado no Firebase Console ou mesmo no próprio app, usando o botão Registrar.

6 – Realizando o Logout

Para terminar este primeiro artigo sobre Firebase e concluir todo o processo de autenticação, devemos permitir que o usuário consiga ‘sair do app’, pois do jeito que está hoje, você se autentica uma vez e ele fica sempre logado. Para isso, vamos usar o menu superior direito da tela MainActivity, onde hoje há apenas um menu falso de Settings.

Vamos começar trocando o texto de Settings para Sair. Vá na pasta res/menue altere o arquivo menu_main.xml para ter um id e texto mais condizente com um menu Sair:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="br.com.luiztools.forcavendas.MainActivity">
    <item
        android:id="@+id/action_exit"
        android:orderInCategory="100"
        android:title="Sair"
        app:showAsAction="never" />
</menu>

E agora, na MainActivity.java, procure pelo método onOptionsItemSelected, que faz o tratamento de itens selecionados de menu, alterando-o para que caso o item de Sair seja selecionado. Vamos pedir ao Firebase que faça o signout e depois redirecionamos o usuário de volta à tela de Login:

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_exit) {
            mAuth.signOut();
            startActivity(new Intent(this, LoginActivity.class));
            finish();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

Teste agora: se autentique usando um usuário já existente e depois selecione a opção Sair no menu superior direito do app para ser direcionado de volta à tela de Login.

Com isso, finalizamos o primeiro de uma série de artigos sobre a plataforma Firebase que devem aparecer aqui no blog. Espero que tenham gostado!

Quer ler o próximo? Clique neste link!