Como Implementar o Account Kit Login em Seu Aplicativo Android
(17403) (14)
CategoriasAndroid, Design, Protótipo
AutorVinÃcius Thiengo
VÃdeo aulas186
Tempo15 horas
ExercÃciosSim
CertificadoSim
CategoriaDesenvolvimento Web
Autor(es)Robert C. Martin
EditoraAlta Books
Edição1ª
Ano2023
Páginas416
Tudo bem?
Neste artigo vamos trabalhar com a API de autenticação do Facebook, a Account Kit API. Com isso vamos a implementação de um sistema de login completo utilizando poucas linhas de código, assim teremos mais tempo de desenvolvimento nos problemas exclusivos do domínio de nosso aplicativo Android.
Com isso, digo, com a construção de um projeto com o Account Kit, você aprenderá também como utilizar essa API em seus próprios aplicativos.
Nosso projeto de exemplo será um protótipo de um aplicativo de Blog onde nosso objetivo, devido a fama e crescimento do APP, é fazer com que os usuários somente consigam acesso ao conteúdo depois de realizar o login nele.
Isso para que possamos, futuramente, enviar a eles algumas promoções por email. Fique tranquilo caso tenha ficado confuso o domínio do problema com a API em estudo, no decorrer do conteúdo vamos ir mais ao detalhe.
Antes de prosseguir, não esqueça de se inscrever 📫na lista de e-mails do Blog para receber, em primeira mão, todas as atualizações do dev Android.
A seguir os tópicos que serão abordados:
- Por que o Account Kit?:
- Projeto de exemplo, back-end Web:
- Projeto de exemplo, Android:
- Implementação de login via Account Kit:
- Criando uma conta de desenvolvedor Facebook;
- Criando uma nova conta de aplicativo no Facebook Developers;
- Atualizações AndroidManifest e classe Application;
- Atualizações Gradle App Level;
- Adição de tema do Account Kit;
- Configuração da Activity de login;
- Atualização das classes de modelo;
- Customização do email de confirmação;
- Testes e resultados.
- Vídeo com implementação passo a passo do projeto;
- Conclusão;
- Fontes.
Por que o Account Kit?
Se você é desenvolvedor de software, principalmente Full Stack, provavelmente já teve de criar o código da lógica de login de algum sistema. E, posteriormente, a lógica de login, novamente, de algum outro software seu ou de cliente.
Se parar para analisar, muito da lógica é quase a mesma. A parte criativa vem somente nos algoritmos exclusivos do domínio do projeto de software.
Hoje em dia temos várias APIs que aplicam o que é padrão em nossos códigos e então nos dão ainda mais tempo nas soluções dos algoritmos de domínio. O Account Kit é uma dessas APIs, porém para o código de login.
Sistemas de Login
Nos sistemas de login convencionais o que temos é: primeiro um formulário de cadastro e posteriormente a possibilidade de login.
Esse fluxo é um pouco amenizado com os logins sociais, logins que as maiores redes permitem utilizar via API proprietária.
O problema dos logins sociais é a necessidade de o usuário ter de ser cadastrado na rede social da API de login em uso.
Acredite, alguns usuários não têm conta em algumas redes sociais e outros podem não confiar em seu aplicativo a ponto de realizar o login social dentro dele.
Resumo. Sem login social você, muito provavelmente, não conseguirá escapar da implementação dos itens a seguir:
- Formulário de cadastro, com criptografia para a senha;
- Email ou SMS de confirmação de conta;
- Formulário de login, também com criptografia para a senha;
- Formulário de recuperação de acesso (perda de senha ou login).
Note que os itens anteriores envolvem também codificação em ao menos um back-end Web.
Com o login social os problemas já foram apresentados e, mesmo sendo poucos, eles são suficientes para deixarem uma boa parte de usuários de sistemas mobile fora de seus aplicativos Android.
O problema dos campos a mais em formulários
Apesar de já discutido aqui no Blog, mais precisamente no artigo API de Endereços Para Pré-Cadastro em APPs Android. Vale ressaltar a importância da definição correta dos campos que vão estar em um formulário.
Saber que para ter acesso a um determinado conteúdo é preciso primeiro criar uma conta, depois confirma-la e ai sim acessa-la via login para finalmente poder obter o conteúdo tão desejado. Saber disso é cansativo, mesmo que seja o caminho mais comum.
Sem contar que todo esse fluxo pode ser ainda pior do que ter somente um grande formulário com cadastro e confirmação em um único lugar. Note que já sabemos, por meio de conteúdos de testes no marketing digital, que quanto mais campos em um formulário, menor o número de cadastros.
Muitos campos e passos é, provavelmente, sinônimo de um número ainda menor de inscritos, clientes e usuários.
Ok, mas podemos utilizar o login do Firebase para amenizar ainda mais os cadastros e não exigir vinculo com redes sociais, certo?
Sim. Mas você ainda não conseguirá evitar o formulário de cadastro com, se me lembro bem, pelo menos dois campos: email e senha.
Com sistemas mobile, podemos melhorar ainda mais o fluxo de login, podemos obter mais informações do usuário posteriormente somente depois do cadastro já realizado, isso sem ter ao menos um formulário de cadastro.
Ok, agora as coisas devem ter ficado confusas, vamos ao Account Kit.
Visão geral
Em 2016 o Facebook liberou o Account Kit API, esse que tem como objetivo facilitar o processo de login dos usuários de aplicativos Android, IOS e Web.
Mesmo tendo suporte também para aplicativos Web, segundo a documentação o foco mesmo é para sistemas mobile, onde a facilidade é ainda maior quando trabalhando somente com Token de acesso, sem o uso de código de servidor (no decorrer do conteúdo você vai entender mais sobre isso).
As vantagens mais evidentes no uso do Account Kit são:
- Apenas um campo deve ser fornecido (Email ou Telefone);
- Cadastro e login estão no mesmo passo no fluxo de acesso;
- Email e SMS de confirmação de conta são gratuitos e gerenciados pelo próprio Facebook;
- O usuário não precisa ter conta no Facebook e mesmo se tiver não haverá conflitos de dados;
- Possibilidade de customização;
- API simples de implementar.
As imagens anteriores, digo, a que tem uma Activity de login por telefone e outra por email, estas são do aplicativo de exemplo que vamos construir e lhe adianto que estas estão com configurações de layout customizadas, pois a tela padrão é como a seguinte:
Sobre as mensagens de confirmação gratuitas, segundo o Facebook, a partir de agosto de 2018, pode ser que essas mensagens tenham um custo, isso caso seu aplicativo passe dos 100.000 envios mensais.
Caso seu APP seja de uma startup que faça parte do FBStart, é certo que não haverá cobrança, eles afirmam isso na documentação.
Alias, a documentação é algo a se destacar, pois ela está também, por completo, em português do Brasil. Devido a isso vamos aqui trabalhar somente um domínio do problema com o Account Kit, pois ele oferece ainda mais possibilidades.
Logo, depois deste artigo, não deixe de visitar a documentação em: https://developers.facebook.com/docs/accountkit.
Não deixe de ler também as "Perguntas frequentes" para saber de algumas limitações que ainda existem na API.
Voltando ao fluxo de login, com o Account Kit o flow é o seguinte:
Nós vamos manter nossos códigos de domínio em ActivityConteudo, que na verdade representa várias Activities e outras entidades de nosso projeto. E então apenas devemos colocar o código padrão, da API Account Kit, em ActivityLogin para invocar a tela de login correta.
Com isso podemos partir para a implementação do exemplo.
Projeto de exemplo, back-end Web
Como explicado no início do artigo, nosso domínio do problema é o domínio de um Blog. Aqui vamos construir um projeto que simula um aplicativo em produção. Porém somente as partes necessárias a nós na apresentação do uso do Account Kit é que serão construídas.
Note que o back-end Web é independente do Account Kit, mesmo que seja possível se comunicar com esta API direto dessa parte do projeto. Aqui não precisaremos dessa comunicação. Logo, você está livre para seguir com sua própria configuração de back-end, mas recomendo que continue como está aqui, para melhor entender a API e o projeto de exemplo.
Para acesso rápido ao código desta seção, entre no GitHub dele em: https://github.com/viniciusthiengo/blog-app-article-project-web.
A seguir as configurações utilizadas para a construção do lado Web do projeto:
- Apache 2.2.29;
- PHP 5.6.2;
- PHPStorm 10.0.3.
Você está livre para utilizar o IDE ou editor de código de sua preferência.
Como estrutura final do projeto lado Web teremos:
Note que para facilitar o exemplo estaremos trabalhando com uma base de dados JSON, a seguir mais sobre ela.
Banco de dados JSON e classe de gerência de dados
Seguindo há a base de dados JSON que estaremos utilizando para carregar os artigos do aplicativo de Blog. Base /data/posts.json:
[
{
"id": 1,
"titulo": "MVP Android",
"uriImagem": "https://www.thiengo.com.br/img/post/80-80/mvp-android.png",
"sumario": "Entenda o que é e como utilizar o padrão de arquitetura Model-View-Presenter em aplicativos Android, confira.",
"ehFavorito": false
},
{
"id": 2,
"titulo": "Como Colocar Notificações Bolha em Seu Aplicativo Android",
"uriImagem": "https://www.thiengo.com.br/img/post/80-80/como-colocar-notificacoes-bolha-em-seu-aplicativo-android.png",
"sumario": "Aprenda, passo a passo, como colocar notificações bolha (Floating Windows) em seus aplicativos Android, para melhor apresentar conteúdos não visualizados. Confira.",
"ehFavorito": true
},
{
"id": 3,
"titulo": "Top 10 leituras de 2016 que são boas pedidas para 2017",
"uriImagem": "https://www.thiengo.com.br/img/post/80-80/top-10-leituras-de-2016-que-sao-boas-pedidas-para-2017.png",
"sumario": "10 excelentes leituras de 2016, do Blog, que podem fazer parte de sua biblioteca e aumento de produção em 2017, confira.",
"ehFavorito": false
},
{
"id": 4,
"titulo": "AndroidAnnotations, Entendendo e Utilizando",
"uriImagem": "https://www.thiengo.com.br/img/post/80-80/androidannotations-entendendo-e-utilizando.png",
"sumario": "Melhore a leitura do código de sua APP Android utilizando anotações para construção de scripts padrões que não fazem parte da lógica de negócio, confira.",
"ehFavorito": true
},
{
"id": 5,
"titulo": "Estudando Android - Lista de Conteúdos do Blog",
"uriImagem": "https://www.thiengo.com.br/img/post/80-80/estudando-android-lista-de-conteudos-do-blog.png",
"sumario": "Estude pela lista, ordenada, de conteúdos em texto e em vídeo, do Blog, para você aprender a construir seus próprios aplicativos Android.",
"ehFavorito": false
},
{
"id": 6,
"titulo": "GCMNetworkManager Para Execução de Tarefas no Background Android",
"uriImagem": "https://www.thiengo.com.br/img/post/80-80/gcmnetworkmanager-para-execucao-de-tarefas-no-background-android.png",
"sumario": "Aprenda a criar um simples aplicativo Android, de GPS tracking, utilizando, para tarefas de background, o GCMNetworkManager.",
"ehFavorito": false
}
]
O arquivo /data/criar-json-database.php e a classe MockData foram utilizados para que fosse possível criar a base JSON /data/posts.json. Não vou colocar o código destes aqui, pois eles não são necessários para o funcionamento depois da base já apresentada anteriormente. No GitHub do projeto você tem acesso a estas entidades.
A seguir a classe que gerencia o acesso as bases de dados, classe Database:
class Database
{
public static function saveDatabase( $database, $objetos ){
$database = fopen( $database, 'w' );
fwrite( $database, json_encode($objetos) );
fclose( $database );
}
public static function getDados($database ){
$dadosString = file_get_contents( $database );
$objetos = json_decode($dadosString);
return $objetos;
}
}
As bases de dados?
Sim, pois vamos permitir o armazenamento de dados de email dos usuários do aplicativo. O back-end Web já estará pronto para receber esse dado de cada usuário, mas ele funciona sem problema algum caso o email do usuário ainda não seja enviado.
Essa base de dados, /data/users.json, terá o seguinte formato:
[
{
"email": "usuario_1@gmail.com",
"data": "2017/02/12 11"
},
{
"email": "usuario_2@gmail.com",
"data": "2017/02/12 16"
}
]
Ela será utilizada somente pelo administrador do Blog, para coletar os emails destes usuários para futuros envios. Não terá interface Web e mobile para isso, apenas uma requisição no navegador.
Note que aqui, pelo foco ser no código Android, já vamos apresentar o back-end Web por completo, mas no algoritmo do aplicativo mobile vamos primeiro ao código sem interface de login e logo depois vamos passo a passo aplicando o Account Kit, como feito no vídeo no final do artigo.
Classes de domínio
Como classes de domínio temos: AplPost, Post e User. Vamos iniciar com código de Post:
class Post
{
public $id;
public $titulo;
public $uriImagem;
public $sumario;
public $ehFavorito;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
}
public function getTitulo()
{
return $this->titulo;
}
public function setTitulo($titulo)
{
$this->titulo = $titulo;
}
public function getUriImagem()
{
return $this->uriImagem;
}
public function setUriImagem($uriImagem)
{
$this->uriImagem = $uriImagem;
}
public function getSumario()
{
return $this->sumario;
}
public function setSumario($sumario)
{
$this->sumario = $sumario;
}
public function getEhFavorito()
{
return $this->ehFavorito;
}
public function setEhFavorito($ehFavorito)
{
if( $ehFavorito === 'true' || $ehFavorito === true || $ehFavorito === 1 ){
$this->ehFavorito = true;
}
else{
$this->ehFavorito = false;
}
}
}
Mesmo que você esteja utilizando alguma outra linguagem de back-end, a classe anterior é bem simples de entender e assim converter para a linguagem em uso em seu ambiente de desenvolvimento.
Note que todos os métodos getter e setter estão presentes devido ao código de MockData, código que você pode acessar no GitHub do projeto.
A seguir a classe User:
class User
{
public $email;
public $data;
public function __construct( $email )
{
$this->email = $email;
$this->data = date('Y\/m\/d H');
}
public function ehEmail(){
return !filter_var($this->email, FILTER_VALIDATE_EMAIL) === false ;
}
}
Na base de dados, de emails de usuários do aplicativo de Blog, nós vamos estar guardando também a data e horário de inscrição, isso para posterior análise de dados, algo provável caso o aplicativo realmente fosse liberado em produção.
E por fim a classe AplAdmin, classe responsável por intermediar as comunicações entre a classe de controle e a camada de dados. Segue:
include '../data/Database.php';
class AplAdmin
{
public static function getPostsComoJson(){
$objetos = Database::getDados( '../data/posts.json' );
return json_encode( $objetos );
}
public static function getUsersComoJson(){
$objetos = Database::getDados( '../data/users.json' );
return json_encode( $objetos );
}
public static function saveUser( User $user ){
if( !$user->ehEmail() ){
/* EMAIL INVÁLIDO */
return;
}
$users = Database::getDados( '../data/users.json' );
foreach( $users as $u ){
if( strcasecmp( $u->email, $user->email ) == 0 ){
/* EMAIL JÁ SALVO */
return;
}
}
/* SALVANDO NOVO EMAIL */
$users[] = $user;
Database::saveDatabase( '../data/users.json', $users );
}
}
Note que em saveUser() nós primeiro acessamos todos os emails já salvos, isso para podermos verificar se o email atual já está na base, caso sim, ele não será salvo. Caso contrário, o salvamos.
Classe de controle
Nesta seção temos apenas uma classe, CtrlAdmin, responsável por receber as requisições do aplicativo Android e então retornar os dados corretos para ele. Segue código:
/*
* Caso queira encontrar alguns erros em sua aplicação back-end,
* descomente a linha abaixo.
* */
/*ini_set('display_errors', 1);*/
include '../domain/User.php';
include '../domain/Post.php';
include '../apl/AplAdmin.php';
/*
* A superglobal GET é para quando estiver realizando testes pelo navegador
* para não precisar configurar toda a APP para simples testes no back-end.
* */
$dados = isset($_POST['metodo']) ? $_POST : $_GET;
if( strcasecmp( $dados['metodo'], 'get-posts' ) == 0 ){
sleep(1);
$user = new User( $dados['email'] );
AplAdmin::saveUser($user);
$postsJson = AplAdmin::getPostsComoJson();
echo $postsJson;
}
else if( strcasecmp( $dados['metodo'], 'get-users' ) == 0 ){
$usersJson = AplAdmin::getUsersComoJson();
echo $usersJson;
}
Note que como já feito em artigos anteriores aqui no Blog, o ini_set() está "no ponto" para você debugar os possíveis erros que podem ocorrer em sua versão de projeto.
Lembrando também que o código de CtrlAdmin está preparado para testes pelo navegador, por isso a verificação também da superglobal $_GET.
Projeto de exemplo, Android
Em nosso projeto Android, um aplicativo de Blog, vamos apenas apresentar a lista de artigos ativos, artigos que vão vir do back-end Web. Sem ação nos itens e ícones, isso, pois não há necessidade de colocarmos códigos extras para apresentar o Account Kit funcionando em um aplicativo que apenas simula um APP em produção.
Vamos primeiro ao projeto completo, sem a implementação da API do Account Kit, e então vamos as atualizações necessárias. Inicie criando um novo projeto no Android Studio, um com uma "Empty Activity".
Coloque como API mínima a API 15 (isso devido ao Facebook SDK) e como nome: Blog APP.
Ao final desta primeira parte da implementação teremos o seguinte aplicativo:
Para ter acesso ao projeto completo, digo, incluindo arquivos de configuração do Android Studio e as imagens do aplicativo, acesse o GitHub dele em: https://github.com/viniciusthiengo/blog-app-article-project.
Configurações Gradle
A seguir o código do Gradle Project Level ou build.gradle (Project: BlogAPP):
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Não houve modificações e nem haverá no Gradle Project Level.
A seguir o Gradle Module Level, ou build.gradle (Module: app):
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "24.0.3"
defaultConfig {
applicationId "br.com.thiengo.blogapp"
minSdkVersion 15
targetSdkVersion 25
versionCode 2
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.1.1'
testCompile 'junit:junit:4.12'
compile 'com.android.support:design:25.1.1'
/* CARREGAR IMAGENS REMOTAS */
compile 'com.squareup.picasso:picasso:2.5.2'
/* CONEXÃO REMOTA E PARSER JSON */
compile 'com.loopj.android:android-async-http:1.4.9'
compile 'com.google.code.gson:gson:2.7'
}
Os trechos adicionados foram comentados, porém ainda não colocamos o código referente ao Facebook SDK e ao Account Kit API, esses vamos adicionar nas seções de implementação do Account API.
Configurações AndroidManifest
A seguir a configuração inicial do AndroidManifest.xml, ainda sem os códigos do Account Kit e Facebook SDK. Segue:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="br.com.thiengo.blogapp">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar">
<activity android:name=".view.PostsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Da configuração padrão, somente a permissão de Internet foi adicionada.
Configurações de estilo
Assim os arquivos iniciais de estilo. Foram modificados somente o arquivo colors.xml e o arquivo styles.xml para versões de Android API abaixo da 21.
Iniciando com o /res/values/colors.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimaryDark">#388E3C</color>
<color name="colorPrimary">#4CAF50</color>
<color name="colorPrimaryText">@android:color/white</color>
<color name="colorPrimaryLight">#C8E6C9</color>
<color name="colorAccent">#8BC34A</color>
<color name="colorActivityBackground">#ffffff</color>
</resources>
Logo depois o arquivo de String, /res/values/strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Blog APP</string>
</resources>
Agora o arquivo de definição de estilo do tema, /res/values/styles.xml. Isso para aparelhos com Android API abaixo da API 21:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light">
<item name="android:windowBackground">@drawable/background</item>
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
</style>
<style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
<style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>
E por fim o código de definição de estilo do tema para devices com a API partindo da API 21. Segue /res/values-v21/styles.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppTheme.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>
Classes da camada Model
Note que aqui estamos trabalhando com o padrão de estrutura Model-View-Presenter, caso ainda não o conheça, logo depois deste artigo não deixe de acessar um conteúdo completo que tenho sobre ele aqui no Blog: MVP Android.
Assim vamos as classes da camada de modelo, começando pela classe responsável pelo gerenciamento das respostas de requisições ao back-end Web, JsonHttpRequest:
public class JsonHttpRequest extends JsonHttpResponseHandler {
public static final String URI = "http://seu_host/blog-app/ctrl/CtrlAdmin.php";
public static final String METODO_KEY = "metodo";
public static final String METODO_POSTS = "get-posts";
private PresenterMain presenter;
public JsonHttpRequest( PresenterMain presenter ){
this.presenter = presenter;
}
@Override
public void onStart() {
presenter.showProgressBar( true );
}
@Override
public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
Gson gson = new Gson();
ArrayList<Post> posts = new ArrayList<>();
Post p;
for( int i = 0; i < response.length(); i++ ){
try{
p = gson.fromJson( response.getJSONObject( i ).toString(), Post.class );
posts.add( p );
}
catch(JSONException e){}
}
presenter.updateListaRecycler( posts );
}
@Override
public void onFinish() {
presenter.showProgressBar( false );
}
}
No código anterior estamos iniciando e finalizando a apresentação do ProgressBar nos métodos corretos. E aplicando o parser JSON assim que os dados neste formato são devolvidos pelo back-end Web.
A última classe de modelo, a responsável pela invocação remota utilizando a API AsyncHttp. Segue código da classe Model:
public class Model {
private AsyncHttpClient asyncHttpClient;
private PresenterMain presenter;
public Model(PresenterMain presenter ){
asyncHttpClient = new AsyncHttpClient();
this.presenter = presenter;
}
public void retrievePosts() {
RequestParams requestParams = new RequestParams();
requestParams.put( JsonHttpRequest.METODO_KEY, JsonHttpRequest.METODO_POSTS );
asyncHttpClient.post( presenter.getContext(),
JsonHttpRequest.URI,
requestParams,
new JsonHttpRequest( presenter ));
}
}
Classes da camada Presenter
Para a camada Presenter temos duas classes, a princípio, pois estaremos criando outra no decorrer do artigo. Vamos iniciar com o código da classe Post:
public class Post implements Parcelable {
public static final String POSTS_KEY = "posts";
private long id;
private String titulo;
private String uriImagem;
private String sumario;
private boolean ehFavorito;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getTitulo() {
return titulo;
}
public void setTitulo(String titulo) {
this.titulo = titulo;
}
public String getUriImagem() {
return uriImagem;
}
public void setUriImagem(String uriImagem) {
this.uriImagem = uriImagem;
}
public String getSumario() {
return sumario;
}
public void setSumario(String sumario) {
this.sumario = sumario;
}
public boolean isEhFavorito() {
return ehFavorito;
}
public void setEhFavorito(boolean ehFavorito) {
this.ehFavorito = ehFavorito;
}
public int getEhFavoritoIcone(){
if( isEhFavorito() ){
return R.drawable.ic_favorito;
}
return R.drawable.ic_nao_favorito;
}
public Post() {}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(this.id);
dest.writeString(this.titulo);
dest.writeString(this.uriImagem);
dest.writeString(this.sumario);
dest.writeByte(this.ehFavorito ? (byte) 1 : (byte) 0);
}
protected Post(Parcel in) {
this.id = in.readLong();
this.titulo = in.readString();
this.uriImagem = in.readString();
this.sumario = in.readString();
this.ehFavorito = in.readByte() != 0;
}
public static final Creator<Post> CREATOR = new Creator<Post>() {
@Override
public Post createFromParcel(Parcel source) {
return new Post(source);
}
@Override
public Post[] newArray(int size) {
return new Post[size];
}
};
}
Implementamos o Parcelable para podermos utilizar o onSaveInstanceState() posteriormente na Activity que conterá a lista de posts.
Então o código de PresenterMain:
public class PresenterMain {
private static PresenterMain instance;
private Model model;
private PostsActivity activity;
private ArrayList<Post> posts = new ArrayList<>();
private PresenterMain(){
model = new Model( this );
}
public static PresenterMain getInstance(){
if( instance == null ){
instance = new PresenterMain();
}
return instance;
}
public void setActivity(PostsActivity activity){
this.activity = activity;
}
public Activity getContext() {
return activity;
}
public void retrievePosts(Bundle savedInstanceState) {
if( savedInstanceState != null ){
posts = savedInstanceState.getParcelableArrayList( Post.POSTS_KEY );
return;
}
model.retrievePosts();
}
public void showProgressBar(boolean status) {
int visibilidade = status ? View.VISIBLE : View.GONE;
activity.showProgressBar( visibilidade );
}
public void updateListaRecycler(Object object) {
List<Post> postsCarregados = (List<Post>) object;
posts.clear();
posts.addAll( postsCarregados );
activity.updateListaRecycler();
showProgressBar( !(posts.size() > 0) );
}
public ArrayList<Post> getPosts() {
return posts;
}
}
A classe anterior tem como objetivo intermediar a comunicação entre a camada de visualização e a camada de modelo, além de ter toda a lógica de formatação de dados que estaria nas Activities, Fragments e outras entidades de visualização.
Classes da camada View
Enfim a camada de visualização. Como nas camadas anteriores, aqui, a princípio, também temos somente duas classes PostsAdapter e PostsActivity.
Vamos iniciar com o layout utilizado em cada item que passa por PostsAdapter, o layout /layout/item_post.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackground"
android:padding="16dp">
<ImageView
android:id="@+id/iv_thumb"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:scaleType="centerCrop" />
<TextView
android:id="@+id/tv_titulo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginBottom="8dp"
android:layout_toEndOf="@+id/iv_thumb"
android:layout_toLeftOf="@+id/iv_favorito"
android:layout_toRightOf="@+id/iv_thumb"
android:layout_toStartOf="@+id/iv_favorito"
android:textColor="#333"
android:textSize="18sp" />
<ImageView
android:id="@+id/iv_favorito"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:src="@drawable/ic_nao_favorito" />
<TextView
android:id="@+id/tv_sumario"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tv_titulo"
android:layout_toEndOf="@+id/iv_thumb"
android:layout_toRightOf="@+id/iv_thumb"
android:ellipsize="end"
android:maxLines="3"
android:textSize="14sp" />
</RelativeLayout>
A seguir o diagrama do layout anterior:
Assim o código Java de PostsAdapter:
public class PostsAdapter extends RecyclerView.Adapter<PostsAdapter.ViewHolder> {
private PostsActivity context;
private List<Post> posts;
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView ivThumb;
ImageView ivFavorito;
TextView tvTitulo;
TextView tvSumario;
ViewHolder(View itemView) {
super(itemView);
ivThumb = (ImageView) itemView.findViewById(R.id.iv_thumb);
ivFavorito = (ImageView) itemView.findViewById(R.id.iv_favorito);
tvTitulo = (TextView) itemView.findViewById(R.id.tv_titulo);
tvSumario = (TextView) itemView.findViewById(R.id.tv_sumario);
}
private void setData( Post post ){
Picasso.with( context )
.load( post.getUriImagem() )
.into( ivThumb );
tvTitulo.setText( post.getTitulo() );
tvSumario.setText( post.getSumario() );
ivFavorito.setImageResource( post.getEhFavoritoIcone() );
}
}
public PostsAdapter(PostsActivity context, List<Post> posts ){
this.context = context;
this.posts = posts;
}
@Override
public PostsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater
.from( context )
.inflate(R.layout.item_post, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.setData( posts.get( position ) );
}
@Override
public int getItemCount() {
return posts.size();
}
}
Nada de complexo, somente definição de valores das Views do layout item_post.xml.
Assim podemos partir para a atividade principal, que aqui tem o rótulo PostsActivity. Vamos iniciar pelo layout, /layout/activity_posts.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<RelativeLayout
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/colorActivityBackground"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_posts"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
</RelativeLayout>
<ProgressBar
android:id="@+id/pb_loading"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:visibility="gone" />
</android.support.design.widget.CoordinatorLayout>
A seguir o diagrama do layout anterior:
Agora o código final do estado inicial do projeto, o código Java de PostsActivity:
public class PostsActivity extends AppCompatActivity {
private PresenterMain presenter;
private PostsAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_posts);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
presenter = PresenterMain.getInstance();
presenter.setActivity( this );
intiViews();
presenter.retrievePosts( savedInstanceState );
}
private void intiViews() {
super.onStart();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_posts);
recyclerView.setHasFixedSize(true);
LinearLayoutManager mLayoutManager = new LinearLayoutManager( this );
recyclerView.setLayoutManager(mLayoutManager);
DividerItemDecoration divider = new DividerItemDecoration(
this,
mLayoutManager.getOrientation());
recyclerView.addItemDecoration( divider );
adapter = new PostsAdapter( this, presenter.getPosts() );
recyclerView.setAdapter( adapter );
}
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putParcelableArrayList(Post.POSTS_KEY, presenter.getPosts());
super.onSaveInstanceState(outState);
}
public void updateListaRecycler(){
adapter.notifyDataSetChanged();
}
public void showProgressBar( int visibilidade ){
findViewById(R.id.pb_loading).setVisibility( visibilidade );
}
}
Recapitulando: se executarmos o aplicativo Blog APP com o código já apresentado até aqui, temos a entrada ao conteúdo sem nenhum pré-requisito, um cadastro, por exemplo:
Implementação do Login via Account Kit
Com nosso aplicativo ganhando cada vez mais acessos e usuários, notamos que é o momento de pedir algo em troca, isso para posteriores permutas.
Devido ao trabalho empregado na criação de cada artigo, definimos que a solicitação de ao menos o email do usuário é algo válido em nosso projeto atual.
Com isso chegamos a conclusão de que a melhor maneira de obtermos o email do usuário antes de permitir o acesso dele ao conteúdo é com um simples sistema de login com preenchimento somente do email.
Logo, vamos a implementação do Facebook Account Kit API.
Criando uma conta de desenvolvedor Facebook
Primeiro, entre na documentação do Account Kit pelo link a seguir: https://developers.facebook.com/docs/accountkit/android.
Logo depois, desça um pouco a página e clique em "Criar conta de desenvolvedor". Em seguida escolha a opção "Sim" e clique em "Cadastre-se":
Com isso, mantenha o Dialog aberto e siga com as instruções da próxima seção.
Criando uma nova conta de aplicativo no Facebook Developers
Seguindo a configuração anterior, clique em "Crie um número de identificação do aplicativo":
Coloque como nome "Blog APP". Informe seu email e em seguida escolha uma categoria, aqui escolhi "Educação". Novamente clique em "Crie um número de identificação do aplicativo":
Entre com o captcha e logo em seguida, na nova página, clique em "Começar" no item Account Kit:
Logo depois clique em "Começar" novamente:
Deixe a nova página carregada aberta e então vamos as outras configurações. A pouco voltaremos nesta nova página.
Atualizações AndroidManifest e classe Application
Antes de prosseguir com a atualização do AndroidManifest.xml e a criação de uma nova classe Application, acesse a página de seu aplicativo no Facebook Developers e copie, do topo da página, o ID do APLICATIVO. Cole em algum lugar de fácil acesso.
Logo depois, clique no item "Account Kit" no menu lateral, caso ainda não esteja nessa página. Então copie o código em Token do cliente do Account Kit. Cole esse código em algum lugar de fácil acesso.
Agora abra o arquivo /res/values/strings.xml e adicione:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Blog APP</string>
<string name="FACEBOOK_APP_ID">1843588339260456</string>
<string name="ACCOUNT_KIT_CLIENT_TOKEN">80590458dd24fe18640887bf70a056e5</string>
<string name="ak_login_protocol_scheme">ak1843588339260456</string>
</resources>
Onde eu coloquei meu Facebook APP ID, 1843588339260456, você coloque o gerado para ti. Note que em ak_login_protocol_scheme realmente temos o ak na frente do Facebook APP ID.
Os dois primeiros códigos são para que seja possível nós utilizarmos o Account Kit junto a um aplicativo registrado nas contas de desenvolvedores do Facebook.
O código ak_login_protocol_scheme é para que seja possível o acesso direto ao aplicativo caso o usuário esteja realizando a confirmação pelo email, isso em um aplicativo de email dentro do mesmo device com seu APP.
Na verdade ak_login_protocol_scheme é somente parte do código necessário para que isso seja possível, o restante, um Intent-Filter, mostraremos a pouco.
Continuando, agora crie uma nova classe, com o nome App e no pacote presenter do projeto:
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
AccountKit.initialize(getApplicationContext());
}
}
Essa classe é para a inicialização do Account Kit. Você pode colocar esse código de inicialização em qualquer outro lugar do projeto, mas o contexto tem que ser o da Application.
Por fim o código atualizado de AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="br.com.thiengo.blogapp">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".presenter.App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar">
<activity android:name=".view.PostsActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.facebook.accountkit.ApplicationName"
android:value="@string/app_name" />
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/FACEBOOK_APP_ID" />
<meta-data
android:name="com.facebook.accountkit.ClientToken"
android:value="@string/ACCOUNT_KIT_CLIENT_TOKEN" />
<activity
android:name="com.facebook.accountkit.ui.AccountKitActivity"
android:theme="@style/LoginCustomTheme"
tools:replace="android:theme">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="@string/ak_login_protocol_scheme" />
</intent-filter>
</activity>
</application>
</manifest>
Dos códigos adicionados, destacados anteriormente, somente o Intent-Filter, a definição de tema na tag <activity> de AccountKitActivity e a definição de uma nova Application class na tag <application> é que são opcionais.
O motivo da classe App você já conhece, discutimos anteriormente. O Intent-Filter é para permitir que o aplicativo seja aberto caso a confirmação por email aconteça no mesmo device de seu APP.
O tema LoginCustomTheme e o atributo tools:replace trabalham em conjunto para que possamos definir um tema customizado para deixarmos a Activity de login com a aparência similar ao de nosso aplicativo inicial.
O tema LoginCustomTheme será definido a pouco em outra seção.
Atualizações Gradle App Level
Agora as atualizações no Gradle Module / App Level. Segue:
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "24.0.3"
defaultConfig {
applicationId "br.com.thiengo.blogapp"
minSdkVersion 15
targetSdkVersion 25
versionCode 2
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
/* PARA QUE SEJA POSSÍVEL A COLETA DE EVENTOS */
repositories {
mavenCentral()
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.1.1'
testCompile 'junit:junit:4.12'
compile 'com.android.support:design:25.1.1'
/* CARREGAR IMAGENS REMOTAS */
compile 'com.squareup.picasso:picasso:2.5.2'
/* CONEXÃO REMOTA E PARSER JSON */
compile 'com.loopj.android:android-async-http:1.4.9'
compile 'com.google.code.gson:gson:2.7'
/* PARA QUE SEJA POSSÍVEL A COLETA DE EVENTOS */
compile 'com.facebook.android:facebook-android-sdk:4.+'
/* REFERENTE AO ACCOUNT KIT */
compile 'com.facebook.android:account-kit-sdk:4.+'
}
Os códigos adicionados: repositories e com.facebook.android:facebook-android-sdk:4.+. Estes códigos são opcionais e referentes ao Facebook SDK. Adicionamos aqui, pois sem o Facebook SDK não é possível a coleta de eventos de login.
Mas fique ciente que somente a inclusão de com.facebook.android:account-kit-sdk:4.+ é o suficiente para continuarmos com a configuração de uso do Account Kit.
A coleta de eventos de login vai nos permitir acessar dados periódicos sobre base de gênero, plataforma, país, versões e outros.
No topo da página de seu aplicativo no Facebook Developers clique em "Visualizar Analytics", em seguida, no menu lateral, clique em "Plataforma do Facebook" e então em "Account Kit". Depois de um tempo de seu aplicativo em uso você terá a apresentação similar ao do dashboard a seguir:
Adição do tema de Account Kit
A seguir a definição do novo tema LoginCustomTheme em /res/values/styles.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
...
<style name="LoginCustomTheme" parent="Theme.AccountKit">
<!-- IMAGEM DE BACKGROUND -->
<item name="com_accountkit_background">@drawable/background</item>
<!-- COR DE BACKGROUND, CASO NÃO DEFINA AO MENOS TRANSPARENTE, A IMAGEM NÃO APARECERÁ -->
<item name="com_accountkit_background_color">#44ffffff</item>
<!-- COR DA TOOLBAR -->
<item name="com_accountkit_primary_color">@color/colorPrimary</item>
<!-- COR DO TEXTO DA TOOLBAR -->
<item name="com_accountkit_primary_text_color">@color/colorPrimaryText</item>
<!-- COR DOS TEXTOS INFORMATIVOS -->
<item name="com_accountkit_secondary_text_color">@color/colorPrimaryText</item>
<!-- COR DA STATUS BAR -->
<item name="com_accountkit_status_bar_color">@color/colorPrimaryDark</item>
<!-- COR DE BACKGROUND DAS VIEWS DE ENTRADA DE DADOS -->
<item name="com_accountkit_input_background_color">@color/colorPrimaryLight</item>
<!-- COR DOS TEXTOS DAS VIEWS DE ENTRADA DE DADOS -->
<item name="com_accountkit_input_text_color">@color/colorPrimaryDark</item>
<!-- COR DE BACKGROUND DOS BUTTONS -->
<item name="com_accountkit_button_background_color">@color/colorPrimaryLight</item>
<!-- COR DOS TEXTOS DOS BUTTONS -->
<item name="com_accountkit_button_text_color">@color/colorPrimaryDark</item>
</style>
</resources>
Assim a Activity de login, onde não podemos mudar os textos, terá ao menos a imagem de background e as cores de acordo com nosso aplicativo de Blog.
Alias essa é uma das regras de negócio do Account Kit, nem mesmo o texto do SMS e do Email, digo, texto de confirmação, é possível alterar.
Note que acessando a documentação você vai ver que é possível mudar ainda mais itens, até mesmo a cor de borda.
Outro ponto importante é que seu tema customizado deve herdar do tema Theme.AccountKit caso você queira utiliza-lo na Activity de login do Account Kit.
Configuração da Activity de login
Com isso vamos a criação de uma nova Activity, mais precisamente, a MainActivity, responsável por conter todo o código que acionará ou a Activity de login do Account Kit ou a PostsActivity para que o usuário tenha acesso ao conteúdo do aplicativo.
Primeiro o código de AndroidManifest.xml com a definição de nossa MainActivity como também sendo a nova Activity de lançamento:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="br.com.thiengo.blogapp">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".presenter.App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar">
<activity android:name=".view.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".view.PostsActivity" />
...
</application>
</manifest>
E enfim a código Java da nova Activity:
public class MainActivity extends AppCompatActivity {
/*
* CÓDIGO INTEIRO ALEATÓRIO PARA POSTERIOR
* VERIFICAÇÃO NO ON-ACTIVITY-RESULT
* */
public static final int APP_REQUEST_CODE = 665;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/* VERIFICA SE O USUÁRIO JÁ ESTÁ CONECTADO */
if (AccountKit.getCurrentAccessToken() != null) {
startActivity(new Intent(this, PostsActivity.class));
finish(); /* REMOVE A ACTIVITY ATUAL DA PILHA DE ACTIVITIES */
}
else{
onLoginEmail();
}
}
public void onLoginEmail() {
/*
* DEFINIÇÃO COMPLETA PARA QUE SEJA APRESENTADA
* UMA ACTIVITY DE LOGIN COM SOLICITAÇÃO DE EMAIL
* */
Intent intent = new Intent(this, AccountKitActivity.class);
AccountKitConfiguration
.AccountKitConfigurationBuilder configurationBuilder =
new AccountKitConfiguration
.AccountKitConfigurationBuilder(
LoginType.EMAIL,
AccountKitActivity.ResponseType.TOKEN );
intent.putExtra(
AccountKitActivity.ACCOUNT_KIT_ACTIVITY_CONFIGURATION,
configurationBuilder.build() );
startActivityForResult( intent, APP_REQUEST_CODE );
}
@Override
protected void onActivityResult(
final int requestCode,
final int resultCode,
final Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == APP_REQUEST_CODE) {
/* ACESSANDO O RESULTADO DA ACTIVITY DE LOGIN */
AccountKitLoginResult loginResult = data.getParcelableExtra(
AccountKitLoginResult.RESULT_KEY );
if (loginResult.getError() != null) {
String mensagem = loginResult.getError().getErrorType().getMessage();
Toast.makeText( this, mensagem, Toast.LENGTH_LONG ).show();
}
else if (loginResult.wasCancelled()) {
/*
* CASO O BACK ARROW TENHA SIDO PRESSIONADO,
* FECHAMOS A ACTIVITY DE LOGIN E SAÍMOS DO APLICATIVO
* */
finish();
}
else {
/* TUDO CERTO, VAMOS A ACTIVITY DE POSTS DO BLOG */
startActivity(new Intent(this, PostsActivity.class));
finish();
}
}
}
}
No código anterior comentei as principais partes. Todo o código (exceto os de tratamento de erro) é o que chamamos de boilerplate code, ou seja, código padrão da API em uso, não há muito no que diferenciar quando você estiver utilizando o Account Kit em seus aplicativos.
Temos sim mais opções, por exemplo, se em onLoginEmail() tivéssemos utilizado LoginType.PHONE ao invés de LoginType.EMAIL, teríamos uma Activity de login com solicitação do telefone do usuário.
Poderíamos também adicionar algumas permissões para já apresentar os campos de login preenchidos, porém em nosso domínio do problema não houve essa necessidade.
Outro ponto é que ao invés de utilizarmos AccountKitActivity.ResponseType.TOKEN poderíamos ter utilizado AccountKitActivity.ResponseType.CODE.
A diferença é que a primeira constante, TOKEN, tem o tempo de vida ainda maior e não exige a busca de um token no back-end Web, exatamente o que a segunda constante, CODE, precisa caso o seu domínio do problema exija esse tipo de configuração.
Importante ressaltar que não há necessidade de definirmos o setContentView() e também que é preciso a remoção da Activity atual, a que invoca o Activity de login, da pilha de Activities depois do login realizado.
Aqui utilizamos o método finish() para essa última regra de negócio.
Atualização das classes de modelo
Depois do login realizado, precisamos ter acesso ao email do usuário. Lembra de nossas metas para monetização!
Para fazer isso com o AccountKit vamos ter de utilizar um código de conexão assíncrona e como estamos falando de obtenção de dados, esse código deverá estar na camada de modelo. Segue novo código de Model:
public class Model {
...
public void retrievePosts() {
AccountKit.getCurrentAccount(new AccountKitCallback<Account>() {
@Override
public void onSuccess(final Account account) {
Log.i("Log", "getId(): " + account.getId());
Log.i("Log", "getPhoneNumber(): " + account.getPhoneNumber());
Log.i("Log", "getEmail(): " + account.getEmail());
RequestParams requestParams = new RequestParams();
requestParams.put( JsonHttpRequest.METODO_KEY, JsonHttpRequest.METODO_POSTS );
requestParams.put( JsonHttpRequest.EMAIL_KEY, account.getEmail() );
asyncHttpClient.post( presenter.getContext(),
JsonHttpRequest.URI,
requestParams,
new JsonHttpRequest( presenter ));
}
@Override
public void onError(final AccountKitError error) {}
});
}
}
Adicionamos algumas invocações para apresentação nos logs do Android Studio, isso para que você tenha acesso aos dados que está requisitando via Intent e também acesso ao ID único do usuário que está realizando o login.
Diferente do token, o ID é sempre o mesmo para o usuário, o token troca sempre que o tempo de vida dele é vencido.
Agora a adição realizada a classe JsonHttpRequest:
public class JsonHttpRequest extends JsonHttpResponseHandler {
public static final String EMAIL_KEY = "email";
...
}
Seguramente teremos o email do usuário sendo enviado, pois foi a única coisa que pedimos a ele no login, caso contrário esse algoritmo de conexão remota não seria atingido. Abaixo vamos recapitular o código back-end que vai ler e salvar esse email:
...
if( strcasecmp( $dados['metodo'], 'get-posts' ) == 0 ){
sleep(1);
$user = new User( $dados['email'] );
AplAdmin::saveUser($user);
$postsJson = AplAdmin::getPostsComoJson();
echo $postsJson;
}
...
Assim vamos a customização das cores do email de confirmação que será enviado ao usuário em login.
Customização do email de confirmação
Acesse a página de seu aplicativo no Facebook Developers. No menu lateral clique em "Account Kit". Role a página até "Configurações de email" e defina as cores como abaixo:
Isso para o email de confirmação também refletir as cores de nosso aplicativo. Não se preocupe com o idioma do email, ele será enviado de acordo com o idioma do device do usuário ou de acordo com os idiomas que você definiu para uso no Account Kit.
Neste aplicativo de exemplo vamos deixar a definição do idioma de acordo com o idioma do device do usuário.
Antes de partirmos para os testes, lembre que você pode escolher trabalhar com o login via número de telefone ou ambos, digo, seu aplicativo ofereceria uma outra Activity ao usuário, com Buttons para ativação do login via Telefone ou Email.
Lembre também que depois dos testes você deve ativar seu aplicativo. Ativar na página dele no Facebook para o status "Produção". Para isso, acesse a opção "Revisão do Aplicativo" no menu lateral e então coloque como sim "Tornar ... público?":
Vamos aos testes.
Testes e resultados
Dê um "Rebuild Project" em seu aplicativo e então execute-o. Terá uma tela como a seguinte:
Entre com seu email e clique em "Next":
Clicando em "Open your Email" será disparada uma Intent para aplicativos de email. Aqui, por estarmos com o emulador, vamos ao aplicativo de email pelo navegador Web. Assim teremos:
Clique em "Continue" ou no link abaixo desse botão, em ambos os casos a seguinte página será aberta, mesmo se a confirmação tivesse sido feita por aplicativos de email:
Clique em "Log in". Voltando ao aplicativo você terá:
Period! Acessando os logs do Android Studio temos:
...
02-12 22:46:18.466 5340-5340/br.com.thiengo.blogapp I/Log: getId(): 1315034131886868
02-12 22:46:18.467 5340-5340/br.com.thiengo.blogapp I/Log: getPhoneNumber(): null
02-12 22:46:18.467 5340-5340/br.com.thiengo.blogapp I/Log: getEmail(): seu_email@gmail.com
...
Como não solicitamos o telefone, ele aparece como null. No back-end Web, mais precisamente na base de dados /data/users.json, temos:
[
{
"email": "seu_email@gmail.com",
"data": "2017/02/12 22"
}
]
Caso você tivesse escolhido a opção PHONE, teria a seguinte tela de login:
Lembrando que devido ao IntentFilter definido no AndroidManifest.xml:
...
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="@string/ak_login_protocol_scheme" />
</intent-filter>
...
Devido a ele, caso a confirmação tivesse sido por algum aplicativo de email no mesmo device, nosso aplicativo de Blog seria aberto já na parte de artigos, ou seja, com a confirmação do login realizada.
Com isso finalizamos a apresentação da Account Kit API, não deixe de acessar a documentação que está em português e bem enxuta. Com poucas horas você consegue ler ela por completo.
Antes de prosseguir, não esqueça de se inscrever na 📫 lista de e-mails do Blog para receber, semanalmente, os conteúdos Android exclusivos do Blog.
Se inscreva também no canal do Blog em YouTube Thiengo.
Vídeo com implementação passo a passo do projeto
Abaixo o vídeo com a implementação passo a passo do Account Kit no projeto de Blog:
Para acesso ao conteúdo completo do projeto, entre no GitHub dele em:
- Parte Android: https://github.com/viniciusthiengo/blog-app-article-project;
- Parte Web: https://github.com/viniciusthiengo/blog-app-article-project-web.
Conclusão
Independente do domínio do problema, digo, do grau de complexidade, Blog ou uma rede social, por exemplo.
O Account Kit, se você souber trabalhar bem o ID único que ele lhe fornece por cada usuário, essa API vai fazer com que seus aplicativos Android tenham um sistema de login seguro e robusto, lhe aliviando da codificação padrão de login para assim você aumentar o foco na codificação no domínio do projeto.
A não necessidade do usuário ter conta no Facebook incentiva ainda mais o uso do API, mesmo sabendo que a cada acesso sem estar logado o usuário terá de confirmar o login por email ou SMS.
Alias esse pode ser um dos pontos negativos do Account Kit em seu ponto de vista, a necessidade de sempre ter de confirmar a conta, mas na verdade não é, lembre de que não há campo de senha, por isso tem de haver a confirmação, caso contrário todos acessariam contas de qualquer um usuário.
Apesar de a documentação estar em português, não deixe de acessar o link de códigos de exemplo na seção Fontes deste artigo, isso, pois na documentação não está trivial entender onde implementar os códigos da Account Key API.
Note que apesar de termos utilizado aqui um back-end Web, você não precisa desse. É possível salvar alguns dados, String, no Account Kit. Na documentação você encontra mais sobre isso. Também é possível dar ao usuário a opção de logout, AccountKit.logout(). E a opção de remoção de conta.
Um ponto negativo é que com o código do IntentFilter, apresentado no decorrer do projeto, sendo inteiramente opcional. Caso você não o utilize, essa "não utilização" não é refletida nas páginas de confirmação de login por email, até mesmo os labels dos botões indicam que se você seguir clicando o aplicativo será aberto, quando na verdade nada acontece.
A imagem acima é de um teste realizado em um device real, onde o IntentFilter de abertura de aplicativo foi removido. O botão "Abrir aplicativo" não tem ação.
Então é isso, mesmo com o ponto negativo citado anteriormente vale sim ao menos o estudo da possibilidade de uso do Facebook Account Key, para que você não tenha de implementar todo o tradicional código de cadastro, login e recuperação de acesso.
Curtiu o conteúdo? Não esqueça de compartilha-lo. E, por fim, de se inscrever na 📩 lista de e-mails, respondo às suas dúvidas também por lá.
Abraço.
Fontes
GitHub de exemplos com Account Kit
Comentários Facebook