【Android】Firebaseを利用したToDoアプリ開発

#はじめに
はじめまして、Life is Tech! Androidコース、メンターのなべです！
Life is Tech ! Advent Calendar 2018、Day 13ですね！

Day12はちゃーはんの記事でした！
Webは素人ですが、確かにCSS設計と音楽の作り方って似ているように感じました！
Web触ってみたい!（やることいっぱい笑）

Life is Tech !のAndroidアプリプログラミングコースではキャンプとスクールを通して、教科書を用いて基礎を学び、中高生がオリジナルのAndroidアプリケーションを作りリリースする過程に、モノづくりのプロセスやオリジナリティを養うことを目的としています。

今日からは後半戦ですよ！残り半分です。

今回の記事はAndroidで作るToDoアプリ！ですが、アカウントを利用して作るToDoアプリです。

#この記事の対象読者
１つでも引っかかったら読んでみることをお勧めします。

- Firebaseを触ってみたい人
- 普段他のコースでAndroidコース気になっている人
- FirebaseAuthでつまづいている人
- RealtimeDatabase使いたい人
- Firebaseが苦手な人(なんか多いらしい)

今回はAndroid以外のコースを学んでいるメンター、メンバーがいることも見込んで、**1から全部書いています！**
なので興味と時間があれば誰でも開発できますよ！

#Firebaseとは
みなさん、あのGoogleさんのサービス、**Firebase**って知っていますか？

Firebaseは、mBaaS（mobile backend as a Service）と呼ばれる、スマートフォンアプリでよく利用される汎用的な機能をクラウドから提供するサービスの一つです。
あらかじめ決められたjavaコードを書くだけで簡単にサーバーとの情報のやり取りを行ってくれます！
Android/iOSアプリでAnalytics(広告)が出来たり、Notification(通知)の送信管理ができたり、Crashレポートやリアルタイムでデータ更新なんかも盛り込んだいろいろできる…とりあえず**すごいサービス**ということだけわかってもらえれば十分です。
詳しくは[この記事](http://jp.techcrunch.com/2016/05/20/20160518google-turns-firebase-into-its-unified-platform-for-mobile-developers/)とか、[公式ドキュメント](https://firebase.google.com/docs/?hl=ja)を見るといいです。日本でもAndroidエンジニアが増えてきた影響なのかわからないですけど、Firebaseのドキュメントは日本語翻訳された状態のもあります。ありがたいです。また、githubに機能ごとに分けられた[サンプルソース](https://firebase.google.com/docs/samples/)も公開してくれているので導入も簡単です！

# 完成形
機能として、ログインをして、ToDo一覧が見れて、さらに項目の追加、削除機能をつけたアプリを目指します。

![alt](https://i.gyazo.com/a7b3a3d6e1b64a6f231c3e6c2b4c5f98.gif)



# Chapter1 始める前の事前準備
## 1. 環境構築
Androidコースではない方は環境構築からしましょう！
普段Android使っているよ〜な人は新規プロジェクトを作って`3.Firebaseの登録`から！

必要なものは以下の2点です。

- [JDK](https://www.oracle.com/technetwork/java/javase/downloads/index.html)
- [AndroidStudio](http://developer.android.com/sdk/index.html)

詳しくはLife is Tech!のお持込みPC チェック＆準備マニュアルを見てください。

# Chapter 2. 新規プロジェクトの作成
## 2.プロジェクトを作成する
**1. 最初の画面**
"Start a new Android Studio"をクリックしてください。
**2. Create Android Project画面**
"Application name","Company domain"は必須項目です。自由に入力してください。
プロジェクトに関する値を入力して [Next] をクリックします。
**3. Target Android Devices画面**
APIを選ぶのですが、23ぐらいを選ぶのが無難でしょう(偏見)
終わったら、[Next] をクリックします。
**4. Add an Activity to Mobile画面**
"Empty Activity"を選択して[Next] をクリックします。
**5. Configure Activity画面**
そのまま[Finish]でプロジェクト作成です！

## 3. Firebaseの登録
次にFirebaseにプロジェクトを登録しましょう。
Firebaseコンソールは[こちら](https://console.firebase.google.com/)。
Firebaseの設定を途中でAndroidStudioに導入します。やり方は[こちら](https://seesaawiki.jp/w/moonlight_aska/d/Android%a4%cbFirebase%a4%f2%c4%c9%b2%c3%a4%b9%a4%eb)。

初めての製作する方は長くなるのでここでの説明は割愛させていただきます。
[こちら](https://seesaawiki.jp/w/moonlight_aska/d/Firebase%a5%d7%a5%ed%a5%b8%a5%a7%a5%af%a5%c8%a4%f2%ba%ee%c0%ae%a4%b9%a4%eb)の記事を参考にして進めてください。

プロジェクトを制作する時にFirebaseの方からこうしてくださいね、とやり方を教えてくれるので心配はありません。


## 4. ライブラリの追加
まず、AndroidStudio左上、「Android」という部分を押して、一番上の「Project」に変更します。
<img width="305" alt=" 2018-12-05 19.00.54.png" src="https://qiita-image-store.s3.amazonaws.com/0/140475/c3c62421-71db-3c55-0d9f-84a70f622ec2.png">

次に、下図のように アプリ名>app>src>build.gradleを開いて、 dependencies部分を変更しましょう。
<img width="315" alt=" 2018-12-05 19.03.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/140475/ebc45ce2-603e-89cc-4d41-73718efcef66.png">


``` build.gradle

// (割愛)

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'

    implementation 'com.google.firebase:firebase-auth:16.0.5'
    implementation 'com.google.firebase:firebase-core:16.0.6'
    implementation 'com.google.firebase:firebase-database:16.0.5'
    implementation 'com.google.android.gms:play-services-auth:16.0.1'
    implementation "com.firebaseui:firebase-ui-auth:4.2.1"
    //上記5行を追加

    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
repositories {
    mavenCentral()
}
apply plugin: 'com.google.gms.google-services'
//repositoriesからここまでも追記分です。忘れずに。
```


最後にもう一つのbuild.gradleに一部追記します。
<img width="289" alt=" 2018-12-05 21.46.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/140475/bc9dc89f-da42-347d-786e-86366d3b374c.png">

``` build.gradle

buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'com.google.gms:google-services:4.2.0'
        //上の行を追記
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

```



## 5. 権限の追加
最後に、 `アプリ名/app/src/main/AndroidManifest.xml`に一部追記します。

``` AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="パッケージ名">

    <uses-permission android:name="android.permission.INTERNET " />
        <!--上の行を追加-->

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <!--以下、省略-->
```

これで準備完了です！
一旦休憩しましょう:tea:
さてここからはコーディングですよ！Let'sコーディング！

# Chapter3 レイアウトの作成
## 6.Activityレイアウトの作成
今度はAndroidの画面を作っていきますよ！

この記事の開発の流れとして、
1.ToDo機能
2.ログイン機能
3.新規作成機能
の流れで作っていきます。

今回、これらに必要なActivityは4つ作ります！
Activityの作り方は[こちら](http://androidstudio.hatenablog.com/entry/2014/07/25/134600)

| Activity名       |    内容|  
|:-----------------|:------------------|
| MainActivity     | メールアカウントを新規作成・ログインする  |
| LoginActivity    | Googleアカウントでログインする |
| ToDoActivity     | (ログインした後)ToDoを表示させる |
| AddActivity  | (ログインした後)新たなToDoを作成する |

そのほか、ToDoアプリに必要なListViewを追加させるために2つ追加します。

| クラス名       |    内容|  
|:-----------------|:------------------|
| CustomAdapter    |  ArrayAdapterを継承して自作したクラス |
| ToDoData  | ToDoの情報をもつクラス |

各アクティビティにレイアウトを書いていきます。
レイアウトはActivityに必要な4つのレイアウトファイル(Activity作成時に自動に作成されます)、ToDoDataの情報の見た目の部分のレイアウトファイルを作成します。

こちらは`アプリ名/app/src/main/res/layout`内に作ります。

| レイアウト名       |    内容|  
|:-----------------|:------------------|
| activity_login.xml   | Googleアカウントでログインする |
| activity_main.xml   |  メールアカウントを新規作成・ログインする |
| activity_todo.xml  | (ログインした後)ToDoを表示させる |
| activity_add.xml    | (ログインした後)新たなToDoを作成する  |
| card_view.xml  | ListViewに表示させるレイアウト |

それでは、各レイアウトの解説です。

AndroidStudioは感覚的に置けるDesign、コードを書いて確実にレイアウトを書くTextモードがあります。最初はDesignで作成した方がわかりやすいですが、慣れてきたらTextモードで書くといいでしょう。

XCodeを使っている方はDesignの方を使うとそんなに違いを感じずスムーズに作れると思いますよ。

``` 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"
    tools:context=".LoginActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="Googleでログインする" />

            <com.google.android.gms.common.SignInButton
                android:id="@+id/googleLoginButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

        </LinearLayout>

        <Button
            android:id="@+id/mailLoginButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="メールアドレスでログイン・登録する" />

    </LinearLayout>

</RelativeLayout>
```

その他あと３つ書いてしまうと記事が長くなってしまいますので、割愛させていただきます。
**[github](https://github.com/yukiyuki961/ToDoApp/tree/master/app/src/main/res/layout)にアップロードしておりますのでご安心を。**

## 7.Custom Layoutの作成

レイアウトの重要な部分として、カスタムレイアウトの部分をピックアップします。
ListViewに表示する一個だけの項目を書きます。
今回はタイトルと内容を作るアプリにするので、TextViewを2つ作ります。
TextView2つで1セットのレイアウトを作ったという意味です。

``` card_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="80dp"
        android:layout_margin="6dp"
        android:orientation="vertical">


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/title_text_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                android:text="Sample Text"
                android:textAppearance="?android:attr/textAppearanceMedium"
                android:textSize="30sp"
                android:textStyle="bold" />

        </LinearLayout>

        <TextView
            android:id="@+id/content_text_view"
            android:layout_width="match_parent"
            android:layout_height="fill_parent"
            android:layout_gravity="center_horizontal|top"
            android:layout_marginLeft="3dp"
            android:text="Sample Text"
            android:textAppearance="?android:attr/textAppearanceMedium" />

    </LinearLayout>


</LinearLayout>
```

# Chapter4 Activityの作成
## 8.LoginActivity作成
LoginActivityの機能として、

- Googleアカウントでのログイン
- メールでのログインさせるための画面推移

の２点の作成をしました。

まず、FirebaseのAuthenticationのログイン方法の`メール/パスワード`、`Google`を有効にしましょう。
<img width="1680" alt=" 2018-12-10 1.26.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/140475/ffd583b9-7fcd-ce0c-a946-b96f59334ebd.png">

Googleのログイン部分は主にこのように書きます。

``` LoginActivity.java

    public void loginGoogle() {
        mAuth = FirebaseAuth.getInstance();
        // Google ログイン認証
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestIdToken(getString(R.string.default_web_client_id))
                .requestEmail()
                .build();

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .enableAutoManage(this/*FragmentActivity*/, this/*OnConnectionFailedListener*/)
                .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
                .build();

        Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(mGoogleApiClient);
        startActivityForResult(signInIntent, RC_SIGN_IN);
    }
```

`enableAutoManage`メソッドの第一引数はFragmentActivityを継承したActivityを渡します。
`AppCompatActivity`はFragmentActivityのサブクラスですので、thisを渡しました。
第二引数には、`OnConnectionFailedListener`を渡します。このリスナーはGoogleApiClientへの接続に失敗した時にコールバックされます。

続いて、GoogleのSignInButtonをレイアウトして、ログイン時の挙動を設定していきます。

``` LoginActivity.java
SignInButton googleSignInButton = (SignInButton) findViewById(R.id.googleLoginButton);
googleSignInButton.setSize(SignInButton.SIZE_STANDARD);
googleSignInButton.setOnClickListener(this);
```

onClickListenerには先ほど記述したloginGoole()を呼ぶようにしています。

その後、`OnActivityResult`メソッド内でsigninの結果を受け取り、オブジェクトから ID トークンを取得して Firebase 認証情報と交換し、Firebase認証情報を使用して、認証を行います。


``` java
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == RC_SIGN_IN) {
            GoogleSignInResult result = Auth.GoogleSignInApi.getSignInResultFromIntent(data);
            if (result.isSuccess()) {
              //ログインに成功した場合
                GoogleSignInAccount account = result.getSignInAccount();
                firebaseAuthWithGoogle(account);
            } else {
              //ログインに失敗した場合
                System.out.println(result.getStatus());
                Toast.makeText(LoginActivity.this, "Error", Toast.LENGTH_SHORT).show();
            }

        }
    }

    private void firebaseAuthWithGoogle(GoogleSignInAccount acct) {
        AuthCredential credential = GoogleAuthProvider.getCredential(acct.getIdToken(), null);
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {
                            changeActivity();
                            Log.d("LoginActivity", "ログインに成功");
                        } else {
                            Toast.makeText(LoginActivity.this, task.getException().getMessage(), Toast.LENGTH_SHORT).show();
                        }
                    }
                });
    }
```

**LoginActivity全体は[こちら](https://github.com/yukiyuki961/ToDoApp/blob/master/app/src/main/java/com/valjapan/todoapp/LoginActivity.java)**

## 9.MainActivity作成
MainActivityには**メール**での新規作成・ログインの機能をつけています。
Googleのログイン機能と大した変わりはないです。
メールアカウントでの新規作成とログインの作成手順は似ているのでそんなに手間はかかりません。

まずはonCreate内に`FirebaseAuth`オブジェクトの共有インスタンスを取得します。

```MainActivity.java

public class MainActivity extends AppCompatActivity {
    EditText emailFormEditText, passwordFormEditText;
    public Intent data;
    public FirebaseAuth mAuth;
    private static final String TAG = "EmailPassword";

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

        emailFormEditText = (EditText) findViewById(R.id.email_log_in_edit_text);
        passwordFormEditText = (EditText) findViewById(R.id.password_log_in_edit_text);

        mAuth = FirebaseAuth.getInstance();
    }

```

ユーザーがアプリに新規作成(ログイン)したら、そのユーザーのメールアドレスとパスワードを`createUserWithEmailAndPassword` (`signInWithEmailAndPassword`) に渡します。

``` MainActivity.java

    public void addMailButton(View v) {
        createAccount(emailFormEditText.getText().toString(), passwordFormEditText.getText().toString());
        setResult(RESULT_OK, data);
    }

    private void createAccount(String email, String password) {
        Log.d(TAG, "createAccount:" + email);

        if (!checkEmpty()) {
            return;
        }

        mAuth.createUserWithEmailAndPassword(email, password)
                .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {
                            // ログインに成功したら、ログインしたユーザーの情報でUIを更新します。
                            Log.d(TAG, "createUserWithEmail:success");
                            Toast.makeText(MainActivity.this, "新規作成に成功しました！", Toast.LENGTH_SHORT).show();
                            changeActivity();
                        } else {
                            // サインインに失敗した場合は、ユーザーにメッセージを表示します。
                            Log.w(TAG, "createUserWithEmail:failure", task.getException());
                            Toast.makeText(MainActivity.this, "Authentication failed.",
                                    Toast.LENGTH_SHORT).show();
                        }

                    }
                });
    }
```

メールアドレスのフォーマットは、Firebaseの方で判定をしてくれるため、メールアドレスのフォーマットが普通でない(?)場合には、アカウントの作成が弾かれ、エラーを表示してくれます。
![device-2018-12-10-023106.png](https://qiita-image-store.s3.amazonaws.com/0/140475/9b298ee7-3498-080e-892e-7ec716d22eaf.png)

自動で判断してくれるので、コードを書かないと判別してくれないなんてことはないんです。便利ですね！
![device-2018-12-10-023123.png](https://qiita-image-store.s3.amazonaws.com/0/140475/68a2f7bf-802d-b100-cbc2-3fc14af204f0.png)

**MainActivity全体は[こちら](https://github.com/yukiyuki961/ToDoApp/blob/master/app/src/main/java/com/valjapan/todoapp/MainActivity.java)**

## 10.ToDoActivity作成

ついにログインしてからの項目に入ります！

ToDoActivityの要素としては
**ListViewにfirebaseから受け取った情報を表示させること**がメインです！

まず、FirebaseのDatabaseのルールをこのように変更してください。
<img width="1680" alt=" 2018-12-10 2.45.11.png" src="https://qiita-image-store.s3.amazonaws.com/0/140475/3ac90069-5815-3c05-681f-1da03bd9ba49.png">

さて、コードの部分です。

``` ToDoActivity.java
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_to_do);

        //ログイン情報を取得
        user = FirebaseAuth.getInstance().getCurrentUser();

        //user id = Uid を取得する
        uid = user.getUid();

        database = FirebaseDatabase.getInstance();
        reference = database.getReference("users").child(uid);

        mListView = (ListView) findViewById(R.id.list_view);

        //CustomAdapterをセット
        mCustomAdapter = new CustomAdapter(getApplicationContext(), R.layout.card_view, new ArrayList<ToDoData>());
        mListView.setAdapter(mCustomAdapter);

        //LongListenerを設定
        mListView.setOnItemLongClickListener(this);

        //firebaseと同期するリスナー
        reference.addChildEventListener(new ChildEventListener() {
//            データを読み込むときはイベントリスナーを登録して行う。
            @Override
            public void onChildAdded(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
//                アイテムのリストを取得するか、アイテムのリストへの追加がないかリッスンします。
                ToDoData toDoData = dataSnapshot.getValue(ToDoData.class);
                mCustomAdapter.add(toDoData);
                mCustomAdapter.notifyDataSetChanged();
            }

            @Override
            public void onChildChanged(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
//                リスト内のアイテムに対する変更がないかリッスンします。

            }

            @Override
            public void onChildRemoved(@NonNull DataSnapshot dataSnapshot) {
//                リストから削除されるアイテムがないかリッスンします。
                Log.d("ToDoActivity", "onChildRemoved:" + dataSnapshot.getKey());
                ToDoData result = dataSnapshot.getValue(ToDoData.class);
                if (result == null) return;

                ToDoData item = mCustomAdapter.getUserDataKey(result.getFirebaseKey());

                mCustomAdapter.remove(item);
                mCustomAdapter.notifyDataSetChanged();
            }

            @Override
            public void onChildMoved(@NonNull DataSnapshot dataSnapshot, @Nullable String s) {
//                並べ替えリストの項目順に変更がないかリッスンします。
            }

            @Override
            public void onCancelled(@NonNull DatabaseError databaseError) {
//                ログを記録するなどError時の処理を記載する。
            }
        });

    }
```

今回、重要な部分はFirebase RealtimeDatabaseを利用するにあたってのchildイベントについてです。
簡単に書くとイベントリスナーをセットすることで、全取得や更新取得などができるということです。
今回はlistViewに動的に加えるための`onChildAdded`と、DoneしたToDoを削除するための`onChildRemoved`に書いています。
詳しくは[こちら](https://firebase.google.com/docs/database/admin/retrieve-data?hl=ja)

長押しすると削除される削除機能は、`onItemLongClickListener`を使います。

``` ToDoActivity.java
    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
        final ToDoData toDoData = mCustomAdapter.getItem(position);
        uid = user.getUid();

        new AlertDialog.Builder(this)
                .setTitle("Done?")
                .setMessage("この項目を完了しましたか？")
                .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // OK button pressed
                        reference.child(toDoData.getFirebaseKey()).removeValue();
                    }
                })
                .setNegativeButton("No", null)
                .show();

        return false;
    }
```

下の部分でFirebase上の項目を削除しています。
`reference.child(toDoData.getFirebaseKey()).removeValue();`
ListView自体削除しなくていいのか？という疑問があるかもしれませんが、それは`onChildRemoved`に記述されているので問題ありません。
項目が削除されるイメージとして、

1. **端末から削除コードが流れる**
2. **firebase上のToDoの項目が削除される**
3. **`onChildRemoved`が動き指定されたListViewの項目が削除される**

という形になります。これにより、複数端末で同じアカウントでログインしている時、firebaseのDataを使っているため、リアルタイムで削除することが可能になります。

なんとなく、わかりましたか？

**ToDoActivity全体は[こちら](https://github.com/yukiyuki961/ToDoApp/blob/master/app/src/main/java/com/valjapan/todoapp/ToDoActivity.java)**

## 11.AddActivity作成
さて、お次はToDoの項目を追加するためのActivityです！

AddActivityはそんなに重くないです。保存する部分が一番重要です。

``` AddActivity.java
    public void save(View v) {
        String title = titleEditText.getText().toString();
        String context = contentEditText.getText().toString();
        String key = reference.push().getKey();
        FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
        String uid = user.getUid();

//    引数のToDoDataの内容をデータベースに送る。
        ToDoData toDoData = new ToDoData(key, title, context);

        reference.child("users").child(uid).child(key).setValue(toDoData).addOnSuccessListener(new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void v) {
                finish();
            }
        });
    }
```

この部分でコードを送信しています。
`reference.child("users").child(uid).child(key).setValue(toDoData)`
childがディレクトリ構造のような意味をしています。

URL的に見ると、
`https://アプリ名.firebaseio.com/users/ユーザーid/送信時の乱数/ToDoの項目(タイトル等)`
というような形になります。

**AddActivity全体は[こちら](https://github.com/yukiyuki961/ToDoApp/blob/master/app/src/main/java/com/valjapan/todoapp/AddActivity.java)**


## 12.ToDoData作成
もう少しです！頑張って！
次にデータの作成です。簡単にすると"型","テンプレート"といったところでしょうか。

``` ToDoData.java
public class ToDoData {
    public String title;
    public String context;
    public String firebaseKey;

    public ToDoData(String key, String title, String context) {
        this.firebaseKey = key;
        this.title = title;
        this.context = context;
    }

    public ToDoData() {
    }

    public String getFirebaseKey() {
        return firebaseKey;
    }

    public void setFirebaseKey(String firebaseKey) {
        this.firebaseKey = firebaseKey;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContext() {
        return context;
    }

    public void setContext(String context) {
        this.context = context;
    }

}
```

今回の項目はFirebaseKey、Title、Contentの3つなので、それに対応するgetter,setterを作ってあげるだけで問題なしです。

**ToDoData全体は[こちら](https://github.com/yukiyuki961/ToDoApp/blob/master/app/src/main/java/com/valjapan/todoapp/ToDoData.java)**


## 13.CustomAdapter作成
最後です！
CustomAdapterはArrayAdapterを継承して自作したクラスです。

```CustomAdapter.java
public class CustomAdapter extends ArrayAdapter<ToDoData> {
//ArrayAdapter継承クラスはコンストラクタを設置することが義務付けられている。

    private List<ToDoData> mCards;

    public CustomAdapter(Context context, int layoutResourceId, List<ToDoData> toDoData) {
        super(context, layoutResourceId, toDoData);

        this.mCards = toDoData;
    }

    @Override
    public int getCount() {
        return mCards.size();
    }

    @Nullable
    @Override
    public ToDoData getItem(int position) {
        return mCards.get(position);
    }

    @NonNull
    @Override
    public View getView(final int position, @Nullable View convertView, @NonNull final ViewGroup parent) {
        final ViewHolder viewHolder;

        if (convertView != null) {
            viewHolder = (ViewHolder) convertView.getTag();

        } else {
            convertView = LayoutInflater.from(getContext()).inflate(R.layout.card_view, null);
            viewHolder = new ViewHolder();

            viewHolder.titleTextView = (TextView) convertView.findViewById(R.id.title_text_view);
            viewHolder.contentTextView = (TextView) convertView.findViewById(R.id.content_text_view);
            convertView.setTag(viewHolder);

        }

        ToDoData toDoData = mCards.get(position);

        viewHolder.titleTextView.setText(toDoData.getTitle());
        viewHolder.contentTextView.setText(toDoData.getContent());

        return convertView;
    }


    public ToDoData getToDoDataKey(String key) {
        for (ToDoData toDoData : mCards) {
            if (toDoData.getFirebaseKey().equals(key)) {
                return toDoData;
            }
        }

        return null;
    }

    static class ViewHolder {
        TextView titleTextView;
        TextView contentTextView;
    }


}
```

CustomAdapterのitemとなるcard_view.xmlのそれぞれのViewのインスタンスをまとめて管理するためのViewHolderクラスを新しく作ることが大切です。
今回はtitleとcontextを表示させるためのTextView2つをまとめて管理しています。ViewHolderのインスタンスを生成すれば2つのViewのインスタンスをlayoutファイルから生成することができます。

# 実際に起動してみよう
これで遂にアプリが完成です！
お疲れ様です:relaxed::wave:

AndroidStudioにあるエミュレーターでも、お手持ちのAndroidスマートフォンでも起動することが可能です。

<img width="431" alt=" 2018-12-10 4.28.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/140475/d3da7eaa-a27c-82e9-c65e-3c1f3b3952b4.png">

あとはUIですね。このままリリースまで持っていきます！
後々リリースしたデザインとかもおまけに追記します！

FirebaseAuthの他に使うことができる機能として、メールリンク、インスタ、Lineでのログインなどがあるので別な記事に書こうと思います！

# 完成リポジトリ
githubに公開しています。
https://github.com/yukiyuki961/ToDoApp

# おわりに
なんとか記事を完成に持っていけました…。
なかなかQiitaなどに記事を上げる機会が無く、今回はチャレンジの意味を込めての参加でした(笑)

今までもFirebaseを利用してのアプリ開発はありましたが、今までは最初にPushするときのfirebaseのkeyを端末に保存して、そのディレクトリ下に項目を作っていました。
要するに1つの端末でしか閲覧ができなかったというわけです。

ある意味言い返すと同じキーで端末を動かしていたので、他のユーザーが投稿したのが見れるのでSNSの様な感じに捉えることも可能ですが…セキュリティに問題ありですね:sweat_smile:

今回、Firebase Authを利用したアプリケーションの開発をしました！
これによって端末間のリアルタイム同期ができるようになりました。

書く前より１歩技術力が成長した気分です:person_with_blond_hair:

12月ですね。普通に寒くて毎日凍えてます笑
ToDoアプリは作って色々追加してみてもなかなか埋まらないものですね。　

これを機に筋トレの時間増やすか！目指せ-5kg!
Life is Tech!の社員のまるちゃんに「なべ、太った？」と言われもしているので、本格的にやろうと思います笑

ここまで読んでいただきありがとうございました！

明日はDay14!
超イケメンメンターとしさんです！
Swiftの記事のようですね。
最近SwimeeでSwift触って勉強したいと思いました！楽しみです！


以上！　Life is Tech! Androidメンターのなべでした！