Google SignIn API, Firebase Android - Parte 6
(6625) (13)
CategoriasAndroid, Design, Protótipo
AutorVinÃcius Thiengo
VÃdeo aulas186
Tempo15 horas
ExercÃciosSim
CertificadoSim
CategoriaEngenharia de Software
Autor(es)Gene Kim, Jez Humble, John Willis, Patrick Debois
EditoraAlta Books
Edição1ª
Ano2018
Páginas464
Opa, blz?
Nesse post continuamos com a série Firebase Android, dessa vez abordando o login social com o Google SignIn API. Com uma implementação um pouco mais complexa que a do post anterior, Facebook Login.
Note que no final do post tem a implementação completa também detalhada em vídeo e o projeto completo pode ser encontrado no GitHub: https://github.com/viniciusthiengo/nosso-chate
Antes de prosseguir com a implementação abaixo segue a lista dos posts já liberados dessa série:
Facebook Login, Firebase Android - Parte 5
Remoção de Conta e Dados de Login, Firebase Android - Parte 4
Atualização de Dados, Firebase Android - Parte 3
Eventos de Leitura e Firebase UI Android - Parte 2
Persistência Com Firebase Android - Parte 1
Listados os posts, vamos iniciar acessando nossa conta no Console do Google. Assim que acessar terá uma tela similar à tela abaixo:
Clique em "CRIAR PROJETO", entre com o nome de seu projeto (nessa série vamos utilizar "Nosso Chat Firebase"). Então clique em "Criar".
Na página que foi aberta logo depois do projeto ter sido criado, página como a debaixo, por exemplo:
Clique em "Credenciais", logo depois em "Tela de consentimento OAuth". No formulário aberto preencha ao menos os campos "Endereço de e-mail" e "Nome do produto mostrado aos usuários". Então clique em "Salvar".
Você será redirecionado a tela de credenciais novamente, porém ainda não poderá criá-las, pois deverá aguardar alguns segundos (um pouco mais de 1 minuto em meu caso) até que a tela de consentimento seja realmente configurada com os dados que você colocou e salvou. Uma opção é alternar entre abas, por exemplo: clique em "Credenciais" e depois de algum tempo em "Tela de consentimento OAuth" e vice-versa, isso para visualizar o quanto antes que os dados ficarem disposto no formulário de tela de consentimento indicando que já possível seguir com o próximo passo, criar as credenciais.
Na tela credenciais clique em "Criar credenciais" e logo depois em "ID do cliente OAuth". Na tela seguinte escolha "Android", entre com o nome da Client ID (em nosso caso pode ser: "User Nosso Chat Firebase"). Logo depois temos de fornecer nossa SHA1 de desenvolvimento (estamos ainda em ambiente de desenvolvimento).
Para isso temos de fornecer o caminho até o executável "keytool" e o caminho até nossa keystore de debug (válida para qualquer projeto em ambiente de desenvolvimento). Em meu caso fica:
Será necessário entrar com a senha do keystore de debug, no caso é: android
Então a tela seguinte será printada com a SHA1 que precisamos:
Agora com a SHA1 copiada de nosso prompt de comando, volte ao formulário de credencial e cole ela no campo indicado no formulário. Logo depois acesse o projeto Android em seu AndroidStudio e no AndroidManifest.xml, mais precisamente na tag root <manifest>, copie o conteúdo do atributo "package" e cole no campo destinado a ele no formulário de credencial.
Com esse último passo finalizado clique em "Criar". Uma tela com uma chave será printada, copie essa chave e então dê um ok na tela para ela fechar.
Com a chave copiada acesse sua conta no FIrebase, mais precisamente seu projeto. No menu lateral esquerdo clique em "Login & Auth". Depois clique na aba "Google" e então no formulário apresentado cole a chave copiada no campo "Google Client ID". O campo "Google Client Secret" ficará vazio. Para finalizar no dashboard Firebase dê um check em "Enable Google Authentication". Você terá algo similar a:
Nosso próximo passo é verificar se o Google Play Services está integrado ao nosso IDE AndroidStudio, logo, com o IDE aberto, clique em "Tools", "Android" e então em "SDK Manager". Na tela seguinte clique na aba "SDK Tools" e certifique-se de que o Google Play Services, ao menos o mais atual, seteja com um check, caso não, marque e clique em "Apply", logo depois em "Ok". Segue tela:
Veirifcado isso vamos acessar a página de tutorial do Google SignIn Android, mais precisamente a página Start Integrating no link: https://developers.google.com/identity/sign-in/android/start-integrating
Vá até a sessão "Get a configuration file", clique em "GET A CONFIGURATION FILE". Na página seguinte selecione o nome da APP que criou no Console do Google, logo em baixo coloque o nome do package de seu projeto, como fizemos no formulário de criação de credencial anteriormente. Então clique em "Choose and configure services":
Na próxima tela clique em "ENABLE GOOGLE SIGN IN" e então clique em "Generate configuration files". Na próxima tela clique em "Download google-services.json" e emt seguida clique em "Continue Adding Sign-In".
Com o arquivo que realizou o download, coloque ele dentro da pasta /app de seu projeto, como abaixo:
Com isso podemos começar com a atualização dos códigos de nossa APP. Comece atualizando o o gradle top level, mais precisamente o de nome: build.gradle (Project: ThiengoCalopsitaFBExample). Deixe dependencies da seguinte forma:
...
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.google.gms:google-services:2.0.0-alpha6'
}
...
Agora no gradle APP level, mais precisamente o de nome: build.gradle (Module: app). Nesse devemos realizar várias atualizações, então apenas ajuste para ficar como o apresentado abaixo:
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "br.com.thiengo.thiengocalopsitafbexample"
minSdkVersion 16
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
repositories {
mavenCentral()
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
compile 'com.firebase:firebase-client-android:2.5.2+'
compile 'com.firebaseui:firebase-ui:0.3.1'
compile 'com.facebook.android:facebook-android-sdk:[4,5)'
compile 'com.google.android.gms:play-services-auth:8.3.0'
}
Agora você deve estar se perguntando: já temos um google play services auth mais atual que o 8.3.0, por que persistir com o antigo? Até o momento desse post o plugin com.google.gms.google-services ainda não trabalhava com versões mais atuais que o google play services 8.3.0, logo utilize essa configuração se a falha persisitir com versões mais atuais.
Veja que não utilizamos o compile genérico do Google Play Services e sim o especifico para autenticação, sempre utilize somente o necessário, veja os compiles aqui: Setting Up Google Play Services
Nosso próximo passo é iniciar com a configuração do Google SignIn no código Java. Primeiro em nossa activity LoginActivity, em onCreate() coloque os seguintes scripts (somente o depois do comentário "GOOGLE SIG IN"):
...
private GoogleApiClient mGoogleApiClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
/* FACEBOOK */
FacebookSdk.sdkInitialize(getApplicationContext());
callbackManager = CallbackManager.Factory.create();
LoginManager.getInstance().registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
@Override
public void onSuccess(LoginResult loginResult) {
accessFacebookLoginData( loginResult.getAccessToken() );
}
@Override
public void onCancel() {}
@Override
public void onError(FacebookException error) {
showSnackbar( error.getMessage() );
}
});
/* GOOGLE SIGN IN - esse e o codigo novo */
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken("O Web Client ID gerado junto a sua Android Client ID")
.requestEmail()
.build();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
firebase = LibraryClass.getFirebase();
initViews();
verifyUserLogged();
}
...
Para preencher o requestIdToken() acesse novamente o Console do Google, mais precisamente acesse "Credenciais" e então sua tela estará agora similar a:
Copie a hash da coluna "ID do client" da linha "Web client". Então cole em resquestIdToken().
Agora devemos implementar a interface GoogleApiClient.OnConnectionFailedListener, logo nossa LoginActivity terá a seguinte configuração depois dessa implementação:
public class LoginActivity extends CommonActivity
implements GoogleApiClient.OnConnectionFailedListener {
...
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
showSnackbar( connectionResult.getErrorMessage() );
}
}
Caso falho, o showSnackbar() se encarrega de informar o porquê.
Nosso próximo passo é configurar o listener de clique do bottão de login do Google, começamos pelo XML content_login.xml, nesse XML, logo abaixo do botão de login do Facebook coloque o seguinte button:
...
<Button
android:onClick="sendLoginGoogleData"
android:id="@+id/email_sign_in_google_button"
style="?android:textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/action_sign_in_google"
android:textStyle="bold" />
...
No arquivo /res/values/string.xml adicionamos a seguinte nova configuração:
<resources>
...
<string name="action_sign_in_google">Sign in com Google</string>
...
</resources>
Voltando a LoginActivity vamos denifir o método sendLoginGoogleData() logo abaixo de sendLoginFacebookData():
...
public void sendLoginGoogleData( View view ){
Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
startActivityForResult(signInIntent, RC_SIGN_IN_GOOGLE);
}
...
Há um trecho desse código que ainda não implementamos, a constante RC_SIGN_IN_GOOGLE, logo vamos declará-la no topo de nossa LoginActivity:
public class LoginActivity extends CommonActivity implements GoogleApiClient.OnConnectionFailedListener {
private static final int RC_SIGN_IN_GOOGLE = 7859;
...
}
Agora em onActivityResult() devemos filtrar a resposta (requestCode e resultCode) para saber se devemos prosseguir com o login do Google SignIn ou do Facebook, como a versão Facebook não tem requestCode definido por nós, vamos tratar o primeiro condicional para o Google SignIn, o else fica para o Facebook:
...
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if( requestCode == RC_SIGN_IN_GOOGLE
&& resultCode == RESULT_OK ){
GoogleSignInResult googleSignInResult = Auth.GoogleSignInApi.getSignInResultFromIntent( data );
GoogleSignInAccount account = googleSignInResult.getSignInAccount();
syncGoogleSignInToken( account );
}
else{
callbackManager.onActivityResult( requestCode, resultCode, data );
}
}
...
Antes de prosseguir com a implementação de syncGoogleSignInToken() vamos refatorar o método accessFacebookLoginData(), pois o conteúdo dele é muito similar ao que vamos ter para o accessGoogleLoginData(), logo com a refatoração temos:
...
private void accessFacebookLoginData(AccessToken accessToken){
accessLoginData(
"facebook",
(accessToken != null ? accessToken.getToken() : null)
);
}
private void accessGoogleLoginData(String accessToken){
accessLoginData(
"google",
accessToken
);
}
private void accessLoginData( String provider, String token ){
if( token != null ){
firebase.authWithOAuthToken(
provider,
token,
new Firebase.AuthResultHandler() {
@Override
public void onAuthenticated(AuthData authData) {
user.saveTokenSP( LoginActivity.this, authData.getToken() );
user.saveIdSP( LoginActivity.this, authData.getUid() );
user.setId( authData.getUid() );
user.setName( authData.getProviderData().get("displayName").toString() );
user.setEmail( authData.getProviderData().get("email").toString() );
user.saveDB();
callMainActivity();
}
@Override
public void onAuthenticationError(FirebaseError firebaseError) {
showSnackbar( firebaseError.getMessage() );
}
});
}
else{
firebase.unauth();
}
}
...
Note que já implementamos a versão do Google e também utilizamos um operador ternário em accessFacebookLoginData() para facilitar e não termos de utilizar uma longa estrutura condicional somente para uma simples validação.
Como estamos em um post série, a explicação do conteúdo de onAuthenticated() deixo com sua leitura no post: Facebook Login, Firebase Android - Parte 5
Agora sim podemos implementar nosso método syncGoogleSignInToken() que será responsável por obter um token válido para nossa vinculação Google SignIn e Firebase Login. Como assim? E o account.getIdToken() do onActivityResult()?
Bad news! Enquanto o Firebase não atualize a configuração para login utilizando Google Accounts temos de utilizar o token no modelo antigo. O token do account.getIdToken() é apenas uma antecipação a uma possível atualização do código do Firebase, pois esse token não será aceito até o momento desse post.
Estranho não?! Enfim, somente para não gerar ainda mais dor de cabeça caso a atualização venha. Somente de curiosidade, se voltar ao onCreate() de LoginActivity, mais precisamente na variável de configuração gso do tipo GoogleSignInOptions. Nessa variável colocamos a configuração de requestIdToken() justamente para termos esse account.getIdToken() com um valor válido, caso contrário uma string vazia é retornada.
Ok, agora finalmente a implementação de syncGoogleSignInToken(), pode colocar logo abaixo de accessLoginData():
...
private void syncGoogleSignInToken( GoogleSignInAccount googleSignInAccount ){
AsyncTask<GoogleSignInAccount, Void, String> task = new AsyncTask<GoogleSignInAccount, Void, String>() {
@Override
protected String doInBackground(GoogleSignInAccount... params) {
GoogleSignInAccount gsa = params[0];
String scope = "oauth2:profile email";
String token = null;
try {
token = GoogleAuthUtil.getToken( LoginActivity.this, gsa.getEmail(), scope );
} catch (IOException e) {
e.printStackTrace();
} catch (GoogleAuthException e) {
e.printStackTrace();
}
return token;
}
@Override
protected void onPostExecute(String token) {
super.onPostExecute(token);
if( token != null ){
accessGoogleLoginData( token );
}
else{
showSnackbar("Google login falhou, tente novamente");
}
}
};
task.execute(googleSignInAccount);
}
...
Nesse caso precisamos de uma chamada assincrona via AsyncTask, passamos o GoogleSignInAccount como parâmetro, pois precisamos do email da conta, logo a fundamental importância de requisitarmos o email do usuário na difinição das configurações de nosso GoogleSignInOptions em onCreate().
Se está meio confuso sobre o AsyncTask, veja esse post: AsyncTask no Android, Acesso a Thread Principal de Forma Otimizada. Ou a documentação AsyncTask Android Doc
Terminada a requisição, se o token não for null podemos então chamar nosso accessGoogleLoginData(). Note que se houver a atualização do Firebase para melhor atender ao Google SignIn API, nossa chamada em onActivityResult() ficaria da seguinte maneira (além da remoção do método syncGoogleSignInToken()):
...
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if( requestCode == RC_SIGN_IN_GOOGLE
&& resultCode == RESULT_OK ){
GoogleSignInResult googleSignInResult = Auth.GoogleSignInApi.getSignInResultFromIntent( data );
GoogleSignInAccount account = googleSignInResult.getSignInAccount();
accessGoogleDataLogin( account.getIdToken() );
}
else{
callbackManager.onActivityResult( requestCode, resultCode, data );
}
}
...
Agora já podemos rodar a APP. Primeiro vamos em "Build", "Rebuild Project". Logo depois, executando o projeto temos:
Clicando em "SIGN IN COM GOOGLE" uma tela de agreement será printada, permita (allow quando em inglês) e então vamos ter o acesso a MainActivity, nossa área de acesso restrita. Se conferirmos nossa base de dados no Firebase temos uma nova conta, agora com o prefixo "google":
Porém ainda temos de ajustar o menu e o logout em MainActivity. O meno para apresentar somente a opção de logout, assim como fazemos quando o usuário se conecta via Facebook. E o Logout tem de ter uma opção para Google logout também.
Vamos começar atualizando a classe User, mais precisamente o método isSocialNetworkLogged(), pois nela temos a verificação de social network somente para Facebook, vamos acrescentar a verificação do Google:
public class User {
...
public boolean isSocialNetworkLogged( Context context ){
retrieveIdSP( context );
return( this.id.contains("facebook") || this.id.contains("google") );
}
...
}
Agora em onCreate() em nossa MainActivity vamos colocar todoa a configuração e conexão ao Google SignIn API novamente, a mesma que em LoginActivity (olha a repetição de código ai):
...
private GoogleAPlClient mGoogleApiClient;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/* PARA GOOGLE LOGOUT */
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken("O Web Client ID gerado junto a sua Android Client ID")
.requestEmail()
.build();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.enableAutoManage(this, this)
.addApi(Auth.GOOGLE_SIGN_IN_API, gso)
.build();
...
}
...
Temos de implementar novamente a Interface GoogleApiClient.OnConnectionFailedListener, logo nossa MainActivity terá a seguinte configuração com essa Interface:
public class MainActivity extends AppCompatActivity implements GoogleApiClient.OnConnectionFailedListener {
...
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {}
}
Agora nossa última atualização, em onOptionsItemSelected(), adicionando o Google logout:
...
public boolean onOptionsItemSelected(MenuItem item) {
...
else if(id == R.id.action_logout){
firebase.unauth();
LoginManager.getInstance().logOut();
/* GOOGLE LOGOUT */
if( mGoogleApiClient != null && mGoogleApiClient.isConnected() ){
Auth.GoogleSignInApi.signOut(mGoogleApiClient);
}
finish();
}
return super.onOptionsItemSelected(item);
}
...
Dessa forma terminamos a configuração do Google SignIn API e temos então três formas de login na APP de chat, convencional (email e senha), Facebook e Google. O projeto completo pode ser envcontrado no GitHub: https://github.com/viniciusthiengo/nosso-chate
Abaixo o vídeo com a implementação completa, passo a passo:
Fontes:
Firebase Google Authentication
Google SignIn For Android - Start Integration
Setting Up Google Play Services
Vlw
Comentários Facebook