Android Firebase Authentication (EmailとPassword) メモ

Firebase AuthenticationをEmailとPasswordで試してみたのでメモ

公式サンプルを参考にしながら
https://github.com/firebase/quickstart-android/tree/master/auth

# 事前準備

Android Studioでプロジェクト作成
Firebase Consoleでプロジェクト作成

# google-services.jsonの生成
Firebase Consoleで以下の作業を行う。

- Overviewにある「AndroidアプリにFirebaseを追加」をクリック
- Android Studioで作成したアプリのパッケージ名を入力
- 「アプリを追加」をクリック
- `google-services.json`がダウンロードされる

# Firebaseのセットアップ
Android Studioで以下の作業を行う。

## `google-services.json`をモジュール直下に配置

デフォルトなら

```
<PROJECT_ROOT>/app/google-services.json
```

## プロジェクトルートのbuild.gradleを編集

```&lt;PROJECT_ROOT&gt;/build.gradle
buildscript {
  dependencies {
    // 追加
    classpath 'com.google.gms:google-services:3.0.0'
  }
}
```

## モジュールのbuild.gradleを編集

```&lt;PROJECT_ROOT&gt;/&lt;APP_MODULE&gt;/build.gradle
dependencies {
  compile fileTree(dir: 'libs', include: ['*.jar'])
  testCompile 'junit:junit:4.12'
  compile 'com.android.support:appcompat-v7:24.1.1'

  // 以下を追加
  compile 'com.google.firebase:firebase-core:9.2.1'
  compile 'com.google.firebase:firebase-auth:9.2.1'
}

// 追加
apply plugin: 'com.google.gms.google-services'
```

## Manifestの編集

ネットワーク通信のPermissionを追加

```AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
```

# Email・Passwordでの認証機能の実装

ログイン画面のレイアウト作成

```activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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: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=".LoginActivity">

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="textEmailAddress"
        android:ems="10"
        android:id="@+id/emailField"
        android:layout_alignParentTop="true"
        android:hint="E-mail"
        android:layout_alignParentEnd="true"
        android:layout_alignParentStart="true" />

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="textPassword"
        android:ems="10"
        android:id="@+id/passwdField"
        android:layout_below="@+id/emailField"
        android:hint="Password"
        android:layout_alignParentStart="true"
        android:layout_alignEnd="@+id/emailField" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Login by E-mail"
        android:id="@+id/emailLoginButton"
        android:layout_marginTop="31dp"
        android:layout_below="@+id/passwdField"
        android:layout_centerHorizontal="true" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Sign up by E-mail"
        android:id="@+id/emailSignUpButton"
        android:layout_below="@+id/emailLoginButton"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="24dp" />
</RelativeLayout>
```

ログイン画面のActivityを生成

```LoginActivity.java
public class LoginActivity extends AppCompatActivity implements View.OnClickListener {
    private FirebaseAuth mAuth;

    private EditText mEmailField;
    private EditText mPasswdField;

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

        mEmailField = (EditText) findViewById(R.id.emailField);
        mPasswdField = (EditText) findViewById(R.id.passwdField);

        findViewById(R.id.emailLoginButton).setOnClickListener(this);
        findViewById(R.id.emailSignUpButton).setOnClickListener(this);

        mAuth = FirebaseAuth.getInstance();
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.emailLoginButton:
                signIn(mEmailField.getText().toString(), mPasswdField.getText().toString());
                break;
            case R.id.emailSignUpButton:
                createAccount(mEmailField.getText().toString(), mPasswdField.getText().toString());
                break;
        }
    }

    private void createAccount(String email, String password) {
        if (!validateForm()) {
            return;
        }

        mAuth.createUserWithEmailAndPassword(email, password)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {
                            changeActivity();
                        } else {
                            Toast.makeText(LoginActivity.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }

    private void signIn(String email, String password) {
        if (!validateForm()) {
            return;
        }

        mAuth.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {
                            changeActivity();
                        } else {
                            Toast.makeText(LoginActivity.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }

    private boolean validateForm() {
        boolean valid = true;

        String email = mEmailField.getText().toString();
        if (TextUtils.isEmpty(email)) {
            mEmailField.setError("Required.");
            valid = false;
        } else {
            mEmailField.setError(null);
        }

        String password = mPasswdField.getText().toString();
        if (TextUtils.isEmpty(password)) {
            mPasswdField.setError("Required.");
            valid = false;
        } else {
            mPasswdField.setError(null);
        }

        return valid;
    }

    private void changeActivity() {
        Intent intent = new Intent(LoginActivity.this, MainActivity.class);
        startActivity(intent);
    }
}
```

サンプルでは`FirebaseAuth.AuthStateListener`を生成していたが、

今回はログイン状態の変化を検知するつもりは無いので省く

`FirebaseAuth`のメソッドで`createUserWithEmailAndPassword`と`signInWithEmailAndPassword`が用意されているので

それを使うだけでいいみたい

`OnCompleteListener`の`onComplete(@NonNull Task<AuthResult>  task)`

で渡される`task`がエラーメッセージなどを保持しているので

```
Toast.makeText(LoginActivity.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
```

としておけば、エラーメッセージのToastの表示も簡単

ログインした後の画面も作成

```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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: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=".LoginActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Login success!"
        android:id="@+id/textView"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="48dp"
        android:textSize="32dp" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Logout"
        android:id="@+id/logoutButton"
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:textSize="26dp" />
</RelativeLayout>
```

TextViewとログアウトボタンを配置

MainActivityはログアウト処理だけ行うようにした

```MainActivity.java
public class MainActivity extends AppCompatActivity {

    private FirebaseAuth mAuth;

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

        mAuth = FirebaseAuth.getInstance();

        findViewById(R.id.logoutButton).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mAuth.signOut();
                Intent intent = new Intent(MainActivity.this, LoginActivity.class);
                startActivity(intent);
                finish();
            }
        });
    }
}
```

最後にManifestを編集

```AndroidManifest.xml
        <activity android:name=".LoginActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".MainActivity" />
```

これでアプリからSign Upすると、Firebase ConsoleのAuthにユーザが追加されてる