LoginSignup
29
29

More than 5 years have passed since last update.

Kotlin + Firebaseで作るチャットアプリ入門1

Last updated at Posted at 2018-11-17

1. 初めに

Android開発の勉強を始めようと思いスタートしました
Javaではなく、Kotlinでやります。

長くなりそうなため、何回かに分けて投稿します

元ネタ 
【保存版】言語別Youtubeおすすめチャンネル
Kotlin Firebase Messanger

開発言語、環境
・Kotlin
・Android Studio(Target Android9.0 ,API 28)
・Firebase(Authentication,storage,Database)

2. 事前準備

・Firebaseでプロジェクトを作成すること、Googleアカウントがあれば使えるのでさくっと済ませる
 プランなどが数種類あるが無料のsparkでOK
・AndroidStudio側でもプロジェクトを作成する
FirebaseのプロジェクトをAndroidStudioのプロジェクトと接続する
→最初は少し難しく感じた
→Google認証を使うならAuthenticationに設定するデバッグ証明書(sha-1)を先に取得しておいたほうが良い、取得は以下のコマンドで、初期パスワードはandroidらしい

keytool -exportcert -list -v -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore

*コマンドが認識されない場合はJavaSDKのパスを確認する、JREではない

・ライブラリの追加、githubや公式のReadmeを参考に導入、JavaとKotlinでは手順が微妙に違うものあり、気を付けて
 ・groupie
 ・picasso

・データベースの作成
→Realtime Databaseを使用する、データベースは2種類あるので要注意
ユーザーの情報を格納するテーブルとしてusersを作っておく
1.png

→参照ルールは暫定で以下のようにする、読み取りは自由、書き込みは認証通った人のみのイメージ
4.png

・ストレージの作成
→よくあるユーザーのイメージ画像を格納する場所を作っておく
2.png

3. サインアップの画面を作ってみる

外観はこんな感じ
3.png

フォームを丸みを持たせるのはdrawableフォルダに

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@android:color/holo_green_dark"/>
    <corners android:radius="75dp"/>
</shape>

こんな感じのレイアウトを定義したリソースを作っておき、適用したいフォームに下記のように書いてやる

android:background="@drawable/{作ったリソース名}"

続いてロジック

RegisterActivity.kt

package com.example.p3.kotlinmessenger.registerLogin

import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.widget.Toast
import com.example.p3.kotlinmessenger.R
import com.example.p3.kotlinmessenger.messages.LatestMessagesActivity
import com.example.p3.kotlinmessenger.model.User
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.storage.FirebaseStorage
import kotlinx.android.synthetic.main.activity_register.*
import java.util.*

class RegisterActivity : AppCompatActivity() {
    //companion:static変数、メソッドの代わり
    companion object {
        val TAG = "Register Activity"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_register)

        register_button_register.setOnClickListener {
            performRegister()
        }

        already_have_account_test_view.setOnClickListener {
            Log.d(TAG, "Try to show Login Activity")

            //::class.javaでクラスオブジェクトの参照
            val intent = Intent(this, LoginActivity::class.java)
            startActivity(intent)
        }

        selectphoto_button_register.setOnClickListener {
            Log.d(TAG, "Try to show photo selector")
            //ユーザーが連絡先を選択し、アプリが連絡先情報にアクセスできるようにする
            val intent = Intent(Intent.ACTION_PICK)
            intent.type = "image/*"
            startActivityForResult(intent, 0)
        }

    }

    //varの場合、変更が可能
    var selectedPhotoUri: Uri? = null

    //画像取得の終了後に来る処理
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == 0 && resultCode == Activity.RESULT_OK && data != null) {
            Log.d(TAG, "Photo was selected")

            //Bitmapで画像を取得、フォームの周りに黒線もひいとく
            selectedPhotoUri = data.data
            val bitmap = MediaStore.Images.Media.getBitmap(contentResolver, selectedPhotoUri)

            selectphoto_imageview_register.setImageBitmap(bitmap)
            selectphoto_button_register.alpha = 0f

        }
    }

    //Registerボタン押下!
    private fun performRegister() {
        val email = email_edittext_register.text.toString()
        val password = password_edittext_register.text.toString()

        //emailとpwの入力チェック
        if (email.isEmpty() || password.isEmpty()) {
            Toast.makeText(this, "Please enter text in emai/pw", Toast.LENGTH_SHORT).show()
            return
        }

        Log.d(TAG, "Email is: " + email)
        Log.d(TAG, "Password: $password")

        //サインアップ
        FirebaseAuth.getInstance().createUserWithEmailAndPassword(email, password)
            .addOnCompleteListener {
                if (!it.isSuccessful) return@addOnCompleteListener

                //else if Successful
                Log.d(TAG, "Successfully created user with uid: ${it.result?.user?.uid}")
                uploadImageToFireBaseStorage()
            }
            .addOnFailureListener {
                Log.d(TAG, "Failed to create User: ${it.message}")
                Toast.makeText(this, "Failed to create User: ${it.message}", Toast.LENGTH_SHORT).show()
            }
    }

    //ストレージへのプロフィール写真の保存、ユーザー情報をDBへ保存
    //images配下に採番されたファイルIDで登録を行う
    private fun uploadImageToFireBaseStorage() {
        if (selectedPhotoUri == null) return

        val filename = UUID.randomUUID().toString()
        val ref = FirebaseStorage.getInstance().getReference("/images/$filename")

        ref.putFile(selectedPhotoUri!!)
            .addOnSuccessListener {
                Log.d(TAG, "Successfuly uploaded image:${it.metadata?.path}")

                ref.downloadUrl.addOnSuccessListener {
                    Log.d(TAG, "File Location :$it")

                    //画像の保存が完了したタイミングでDBへの登録も行う
                    saveUserToFirebaseDatabase(it.toString())
                }
            }
            .addOnFailureListener {
                //しくじった時、考えないようにする
            }
    }

    //Realtime Databaseにデータを格納
    //users配下にユーザーID単位で登録する
    private fun saveUserToFirebaseDatabase(profileImageUri: String) {
        val uid = FirebaseAuth.getInstance().uid ?: ""
        val ref = FirebaseDatabase.getInstance().getReference("/users/$uid")
        val user = User(uid, username_edittext_register.text.toString(), profileImageUri)

        //Intent.FLAG_ACTIVITY_CLEAR_TASK:Activityのスタックを削除して起動する
        //Intent.FLAG_ACTIVITY_NEW_TASK:タスクがスタックに存在しても新しいタスクとして起動

        ref.setValue(user)
            .addOnSuccessListener {
                Log.d(TAG, "Finally we saved the user to Firebase Database")
                val intent = Intent(this, LatestMessagesActivity::class.java)
                intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK.or(Intent.FLAG_ACTIVITY_NEW_TASK)
                startActivity(intent)
            }
            .addOnFailureListener {
                Log.d(TAG, "Failed to set value to Database")
            }
    }
}


4. 以降

少しずつ続編を投稿する予定です。
一旦このアプリを完成させた後、これをベースに色々作っていこうかと妄想中です。

29
29
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
29