Firebase Android Codelab - สร้างแชทที่เป็นกันเอง

1. ภาพรวม

ภาพหน้าจอ

รูปภาพ: แอป Chat ที่เหมาะกับการทำงาน

ยินดีต้อนรับสู่ Codelab ของ Chat ที่เป็นมิตร ใน Codelab นี้ คุณจะได้เรียนรู้วิธีใช้แพลตฟอร์ม Firebase เพื่อสร้างแอปแชทบน Android

สิ่งที่คุณจะได้เรียนรู้

  • วิธีใช้การตรวจสอบสิทธิ์ของ Firebase เพื่ออนุญาตให้ผู้ใช้ลงชื่อเข้าใช้
  • วิธีซิงค์ข้อมูลโดยใช้ฐานข้อมูลเรียลไทม์ของ Firebase
  • วิธีจัดเก็บไฟล์ไบนารีใน Cloud Storage for Firebase
  • วิธีใช้ชุดโปรแกรมจำลองภายในของ Firebase เพื่อพัฒนาแอป Android ด้วย Firebase

สิ่งที่ต้องมี

  • Android Studio เวอร์ชันล่าสุด
  • โปรแกรมจำลอง Android ที่ใช้ Android 5.0 ขึ้นไป
  • Node.js เวอร์ชัน 10 ขึ้นไป (เพื่อใช้ชุดโปรแกรมจำลอง)
  • Java 8 ขึ้นไป หากต้องการติดตั้ง Java ให้ใช้วิธีการเหล่านี้ เพื่อตรวจสอบเวอร์ชัน ให้เรียกใช้ java -version
  • มีความคุ้นเคยกับภาษาโปรแกรม Kotlin

2. รับโค้ดตัวอย่าง

โคลนที่เก็บ

โคลนที่เก็บ GitHub จากบรรทัดคำสั่งดังนี้

$ git clone https://github.com/firebase/codelab-friendlychat-android

นำเข้าไปยัง Android Studio

ใน Android Studio ให้เลือกไฟล์ > เปิด จากนั้นเลือกไดเรกทอรี build-android-start ( โฟลเดอร์_android_studio) จากไดเรกทอรีที่คุณดาวน์โหลดโค้ดตัวอย่างมา

ตอนนี้คุณควรเปิดโปรเจ็กต์ build-android-start ใน Android Studio แล้ว หากคุณเห็นคำเตือนเกี่ยวกับไฟล์ google-services.json ที่ขาดหายไป ไม่ต้องกังวล โดยเราจะเพิ่มข้อมูลดังกล่าวในขั้นตอนถัดไป

ตรวจสอบทรัพยากร Dependency

ใน Codelab นี้ ระบบได้เพิ่มทรัพยากร Dependency ทั้งหมดให้คุณแล้ว แต่คุณต้องทำความเข้าใจวิธีเพิ่ม Firebase SDK ลงในแอป ดังนี้

build.gradle.kts

plugins {
    id("com.android.application") version "8.0.0" apply false
    id("com.android.library") version "8.0.0" apply false
    id("org.jetbrains.kotlin.android") version "1.8.20" apply false

    // The google-services plugin is required to parse the google-services.json file
    id("com.google.gms.google-services") version "4.3.15" apply false
}

app/build.gradle.kts

plugins {
    id("com.android.application")
    id("kotlin-android")
    id("com.google.gms.google-services")
}

android {
    // ...
}

dependencies {
    // ...

    // Google Sign In SDK
    implementation("com.google.android.gms:play-services-auth:20.5.0")

    // Firebase SDK
    implementation(platform("com.google.firebase:firebase-bom:32.0.0"))
    implementation("com.google.firebase:firebase-database-ktx")
    implementation("com.google.firebase:firebase-storage-ktx")
    implementation("com.google.firebase:firebase-auth-ktx")

    // Firebase UI Library
    implementation("com.firebaseui:firebase-ui-auth:8.0.2")
    implementation("com.firebaseui:firebase-ui-database:8.0.2")
}

3. ติดตั้ง Firebase CLI

ใน Codelab นี้ คุณจะใช้ชุดโปรแกรมจำลอง Firebase เพื่อจำลองการตรวจสอบสิทธิ์ Firebase, Realtime Database และ Cloud Storage ในเครื่อง แพลตฟอร์มนี้จะมอบสภาพแวดล้อมการพัฒนาในพื้นที่ที่ปลอดภัย รวดเร็ว และไม่มีค่าใช้จ่ายเพื่อสร้างแอปของคุณ

ติดตั้ง Firebase CLI

ก่อนอื่นคุณต้องติดตั้ง Firebase CLI หากคุณใช้ macOS หรือ Linux คุณสามารถเรียกใช้คำสั่ง cURL ต่อไปนี้

curl -sL https://firebase.tools | bash

หากใช้ Windows โปรดอ่านวิธีการติดตั้งเพื่อรับไบนารีแบบสแตนด์อโลนหรือติดตั้งผ่าน npm

เมื่อติดตั้ง CLI แล้ว การเรียกใช้ firebase --version ควรรายงานเวอร์ชัน 9.0.0 ขึ้นไป

$ firebase --version
9.0.0

เข้าสู่ระบบ

เรียกใช้ firebase login เพื่อเชื่อมต่อ CLI กับบัญชี Google ของคุณ ซึ่งจะเป็นการเปิดหน้าต่างเบราว์เซอร์ใหม่เพื่อดำเนินขั้นตอนการเข้าสู่ระบบให้เสร็จสมบูรณ์ ตรวจดูว่าได้เลือกบัญชีเดียวกับที่คุณใช้เมื่อสร้างโปรเจ็กต์ Firebase ก่อนหน้านี้

4. เชื่อมต่อกับชุดโปรแกรมจำลอง Firebase

เริ่มโปรแกรมจำลอง

ในเทอร์มินัล ให้เรียกใช้คำสั่งต่อไปนี้จากรากของไดเรกทอรี codelab-friendlychat-android ในเครื่อง

firebase emulators:start --project=demo-friendlychat-android

คุณควรจะเห็นบันทึกลักษณะเช่นนี้ ค่าพอร์ตถูกกำหนดไว้ในไฟล์ firebase.json ซึ่งอยู่ในโค้ดตัวอย่างที่โคลน

$ firebase emulators:start --project=demo-friendlychat-android
i  emulators: Starting emulators: auth, database, storage
i  emulators: Detected demo project ID "demo-friendlychat-android", emulated services will use a demo configuration and attempts to access non-emulated services for this project will fail.
i  database: Database Emulator logging to database-debug.log
i  ui: Emulator UI logging to ui-debug.log

┌─────────────────────────────────────────────────────────────┐
│   All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://localhost:4000                │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬────────────────────────────────┐
│ Emulator        Host:Port       View in Emulator UI            │
├────────────────┼────────────────┼────────────────────────────────┤
│ Authentication  localhost:9099  http://localhost:4000/auth     │
├────────────────┼────────────────┼────────────────────────────────┤
│ Database        localhost:9000  http://localhost:4000/database │
├────────────────┼────────────────┼────────────────────────────────┤
│ Storage         localhost:9199  http://localhost:4000/storage  │
└────────────────┴────────────────┴────────────────────────────────┘
  Emulator Hub running at localhost:4400
  Other reserved ports: 4500

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.

ไปที่ http://localhost:4000 ในเว็บเบราว์เซอร์เพื่อดู UI ของ Firebase Emulator Suite ดังนี้

หน้าแรกของ UI ชุดโปรแกรมจำลอง

ให้คำสั่ง emulators:start ทำงานต่อไปสำหรับ Codelab ที่เหลือ

เชื่อมต่อแอป

ใน Android Studio ให้เปิด MainActivity.kt แล้วเพิ่มโค้ดต่อไปนี้ในเมธอด onCreate

// When running in debug mode, connect to the Firebase Emulator Suite.
// "10.0.2.2" is a special IP address which allows the Android Emulator
// to connect to "localhost" on the host computer. The port values (9xxx)
// must match the values defined in the firebase.json file.
if (BuildConfig.DEBUG) {
    Firebase.database.useEmulator("10.0.2.2", 9000)
    Firebase.auth.useEmulator("10.0.2.2", 9099)
    Firebase.storage.useEmulator("10.0.2.2", 9199)
}

5. เรียกใช้แอปเริ่มต้น

เพิ่ม google-services.json

หากต้องการให้แอป Android เชื่อมต่อกับ Firebase ได้ คุณต้องเพิ่มไฟล์ google-services.json ในโฟลเดอร์ app ของโปรเจ็กต์ Android ตามวัตถุประสงค์ของ Codelab นี้ เราได้จัดเตรียมไฟล์จำลอง JSON ไว้ ซึ่งจะทำให้คุณเชื่อมต่อกับ Firebase Emulator Suite ได้

คัดลอกไฟล์ mock-google-services.json ไปยังโฟลเดอร์ build-android-start/app ในชื่อ google-services.json:

cp mock-google-services.json build-android-start/app/google-services.json

ในขั้นตอนสุดท้ายของ Codelab นี้ คุณจะได้เรียนรู้วิธีการสร้างโปรเจ็กต์ Firebase จริงและแอป Firebase บน Android เพื่อให้แทนที่ไฟล์จำลอง JSON นี้ด้วยการกำหนดค่าของคุณเองได้

เรียกใช้แอป

เมื่อนำเข้าโปรเจ็กต์ลงใน Android Studio และเพิ่มไฟล์ JSON สำหรับการกำหนดค่า Firebase แล้ว คุณก็พร้อมที่จะเรียกใช้แอปเป็นครั้งแรก

  1. เริ่มโปรแกรมจำลองของ Android
  2. ใน Android Studio ให้คลิกเรียกใช้ ( ดำเนินการ) ในแถบเครื่องมือ

แอปจะเปิดขึ้นในโปรแกรมจำลองของ Android ในขั้นตอนนี้ คุณควรเห็นรายการข้อความว่างเปล่า และการส่งและรับข้อความจะไม่ทำงาน ในขั้นตอนถัดไปของ Codelab นี้ คุณจะต้องตรวจสอบสิทธิ์ผู้ใช้เพื่อให้ใช้ Chat ได้

6. เปิดใช้การตรวจสอบสิทธิ์

แอปนี้จะใช้ฐานข้อมูลเรียลไทม์ของ Firebase เพื่อเก็บข้อความแชททั้งหมด ก่อนที่เราจะเพิ่มข้อมูล เราควรตรวจสอบให้แน่ใจว่าแอปปลอดภัย และเฉพาะผู้ใช้ที่ผ่านการตรวจสอบสิทธิ์เท่านั้นที่สามารถโพสต์ข้อความได้ ในขั้นตอนนี้ เราจะเปิดใช้การตรวจสอบสิทธิ์ Firebase และกำหนดค่ากฎความปลอดภัยของฐานข้อมูลเรียลไทม์

เพิ่มฟังก์ชันการลงชื่อเข้าใช้พื้นฐาน

ต่อไปเราจะเพิ่มโค้ดการตรวจสอบสิทธิ์ Firebase พื้นฐานบางอย่างลงในแอปเพื่อตรวจหาผู้ใช้และใช้งานหน้าจอลงชื่อเข้าใช้

ตรวจสอบผู้ใช้ปัจจุบัน

ขั้นแรกให้เพิ่มตัวแปรอินสแตนซ์ต่อไปนี้ลงในคลาส MainActivity.kt

MainActivity.kt

// Firebase instance variables
private lateinit var auth: FirebaseAuth

ตอนนี้เรามาแก้ไข MainActivity เพื่อส่งผู้ใช้ไปยังหน้าจอลงชื่อเข้าใช้ทุกครั้งที่เปิดแอปและไม่ได้ผ่านการตรวจสอบกัน เพิ่มข้อมูลต่อไปนี้ลงในเมธอด onCreate() หลังจากมีการแนบ binding ลงในมุมมอง

MainActivity.kt

// Initialize Firebase Auth and check if the user is signed in
auth = Firebase.auth
if (auth.currentUser == null) {
    // Not signed in, launch the Sign In activity
    startActivity(Intent(this, SignInActivity::class.java))
    finish()
    return
}

นอกจากนี้ เรายังต้องการตรวจสอบด้วยว่าผู้ใช้ลงชื่อเข้าใช้ระหว่างช่วง onStart() หรือไม่

MainActivity.kt

public override fun onStart() {
    super.onStart()
    // Check if user is signed in.
    if (auth.currentUser == null) {
        // Not signed in, launch the Sign In activity
        startActivity(Intent(this, SignInActivity::class.java))
        finish()
        return
    }
}

จากนั้นใช้เมธอด getUserPhotoUrl() และ getUserName() เพื่อแสดงผลข้อมูลที่เหมาะสมเกี่ยวกับผู้ใช้ Firebase ที่ผ่านการตรวจสอบสิทธิ์ในปัจจุบัน ดังนี้

MainActivity.kt

private fun getPhotoUrl(): String? {
    val user = auth.currentUser
    return user?.photoUrl?.toString()
}

private fun getUserName(): String? {
    val user = auth.currentUser
    return if (user != null) {
        user.displayName
    } else ANONYMOUS
}

จากนั้นใช้เมธอด signOut() ในการจัดการปุ่มออกจากระบบ ดังนี้

MainActivity.kt

private fun signOut() {
    AuthUI.getInstance().signOut()
    startActivity(Intent(this, SignInActivity::class.java))
    finish()
}

ตอนนี้เรามีตรรกะทั้งหมดที่พร้อมสำหรับส่งผู้ใช้ไปยังหน้าจอลงชื่อเข้าใช้เมื่อจำเป็น ต่อไปเราจะต้องใช้หน้าจอลงชื่อเข้าใช้เพื่อตรวจสอบสิทธิ์ผู้ใช้อย่างเหมาะสม

ใช้งานหน้าจอลงชื่อเข้าใช้

เปิดไฟล์ SignInActivity.kt ต่อไปนี้เป็นปุ่มลงชื่อเข้าใช้ง่ายๆ ที่ใช้เพื่อเริ่มการตรวจสอบสิทธิ์ ในส่วนนี้ คุณจะได้ใช้ FirebaseUI เพื่อนำตรรกะสำหรับการลงชื่อเข้าใช้ไปใช้

เพิ่มตัวแปรอินสแตนซ์ Auth ในคลาส SignInActivity ใต้ความคิดเห็น // Firebase instance variables:

SignInActivity.kt

// Firebase instance variables
private lateinit var auth: FirebaseAuth

จากนั้นแก้ไขเมธอด onCreate() เพื่อเริ่มต้นใช้งาน Firebase แบบเดียวกับที่คุณทำใน MainActivity

SignInActivity.kt

// Initialize FirebaseAuth
auth = Firebase.auth

เพิ่มช่อง ActivityResultLauncher ใน SignInActivity:

SignInActivity.kt

// ADD THIS
private val signIn: ActivityResultLauncher<Intent> =
        registerForActivityResult(FirebaseAuthUIActivityResultContract(), this::onSignInResult)

override fun onCreate(savedInstanceState: Bundle?) {
    // ...
}

ต่อไป ให้แก้ไขเมธอด onStart() เพื่อเริ่มขั้นตอนการลงชื่อเข้าใช้ FirebaseUI ดังนี้

SignInActivity.kt

public override fun onStart() {
    super.onStart()

    // If there is no signed in user, launch FirebaseUI
    // Otherwise head to MainActivity
    if (Firebase.auth.currentUser == null) {
        // Sign in with FirebaseUI, see docs for more details:
        // https://firebase.google.com/docs/auth/android/firebaseui
        val signInIntent = AuthUI.getInstance()
                .createSignInIntentBuilder()
                .setLogo(R.mipmap.ic_launcher)
                .setAvailableProviders(listOf(
                        AuthUI.IdpConfig.EmailBuilder().build(),
                        AuthUI.IdpConfig.GoogleBuilder().build(),
                ))
                .build()

        signIn.launch(signInIntent)
    } else {
        goToMainActivity()
    }
}

ต่อไป ให้ใช้เมธอด onSignInResult เพื่อจัดการผลการลงชื่อเข้าใช้ หากผลการลงชื่อเข้าใช้สำเร็จ ให้ดำเนินการต่อไปยัง MainActivity:

SignInActivity.kt

private fun onSignInResult(result: FirebaseAuthUIAuthenticationResult) {
    if (result.resultCode == RESULT_OK) {
        Log.d(TAG, "Sign in successful!")
        goToMainActivity()
    } else {
        Toast.makeText(
                this,
                "There was an error signing in",
                Toast.LENGTH_LONG).show()

        val response = result.idpResponse
        if (response == null) {
            Log.w(TAG, "Sign in canceled")
        } else {
            Log.w(TAG, "Sign in error", response.error)
        }
    }
}

เพียงเท่านี้ก็เสร็จแล้ว! คุณได้ใช้การตรวจสอบสิทธิ์กับ FirebaseUI ในการเรียกเมธอดเพียงไม่กี่ครั้ง โดยไม่ต้องจัดการการกำหนดค่าฝั่งเซิร์ฟเวอร์ใดๆ เลย

ทดสอบงานของคุณ

เรียกใช้แอปใน Android Emulator คุณควรไปยังหน้าจอลงชื่อเข้าใช้ทันที แตะปุ่มลงชื่อเข้าใช้ด้วยอีเมล แล้วสร้างบัญชี หากมีการนําทุกอย่างถูกต้อง คุณควรไปยังหน้าจอการรับส่งข้อความ

หลังจากลงชื่อเข้าใช้แล้ว ให้เปิด UI ของ Firebase Emulator Suite ในเบราว์เซอร์ แล้วคลิกแท็บการตรวจสอบสิทธิ์เพื่อดูบัญชีผู้ใช้ที่ลงชื่อเข้าใช้บัญชีแรก

7. อ่านข้อความ

ในขั้นตอนนี้ เราจะเพิ่มฟังก์ชันสำหรับอ่านและแสดงข้อความที่จัดเก็บไว้ใน Realtime Database

นำเข้าข้อความตัวอย่าง

  1. เลือกแท็บฐานข้อมูลเรียลไทม์ใน UI ชุดโปรแกรมจำลองของ Firebase
  2. ลากและวางไฟล์ initial_messages.json จากสำเนาที่เก็บ Codelab ในเครื่องลงในเครื่องมือดูข้อมูล

ตอนนี้คุณควรมีข้อความ 2-3 ข้อความอยู่ใต้โหนด messages ของฐานข้อมูล

อ่านข้อมูล

ซิงค์ข้อมูลข้อความ

ในส่วนนี้ เราจะเพิ่มโค้ดที่ใช้ซิงค์ข้อความที่เพิ่มใหม่ลงใน UI ของแอปโดยทำดังนี้

  • การเริ่มต้นฐานข้อมูลเรียลไทม์ของ Firebase และเพิ่ม Listener เพื่อจัดการกับการเปลี่ยนแปลงที่ทำกับข้อมูล
  • กำลังอัปเดตอะแดปเตอร์ RecyclerView เพื่อแสดงข้อความใหม่
  • การเพิ่มตัวแปรอินสแตนซ์ของฐานข้อมูลพร้อมกับตัวแปรอินสแตนซ์อื่นๆ ของ Firebase ในคลาส MainActivity

MainActivity.kt

// Firebase instance variables
// ...
private lateinit var db: FirebaseDatabase
private lateinit var adapter: FriendlyMessageAdapter

แก้ไขเมธอด onCreate() ของ MainActivity ใต้ความคิดเห็น // Initialize Realtime Database and FirebaseRecyclerAdapter ด้วยโค้ดที่กำหนดไว้ด้านล่าง โค้ดนี้จะเพิ่มข้อความที่มีอยู่ทั้งหมดจาก Realtime Database จากนั้นจะคอยตรวจจับรายการย่อยใหม่ภายใต้เส้นทาง messages ในฐานข้อมูลเรียลไทม์ของ Firebase โดยจะเพิ่มองค์ประกอบใหม่ลงใน UI ของแต่ละข้อความ ดังนี้

MainActivity.kt

// Initialize Realtime Database
db = Firebase.database
val messagesRef = db.reference.child(MESSAGES_CHILD)

// The FirebaseRecyclerAdapter class and options come from the FirebaseUI library
// See: https://github.com/firebase/FirebaseUI-Android
val options = FirebaseRecyclerOptions.Builder<FriendlyMessage>()
    .setQuery(messagesRef, FriendlyMessage::class.java)
    .build()
adapter = FriendlyMessageAdapter(options, getUserName())
binding.progressBar.visibility = ProgressBar.INVISIBLE
manager = LinearLayoutManager(this)
manager.stackFromEnd = true
binding.messageRecyclerView.layoutManager = manager
binding.messageRecyclerView.adapter = adapter

// Scroll down when a new message arrives
// See MyScrollToBottomObserver for details
adapter.registerAdapterDataObserver(
    MyScrollToBottomObserver(binding.messageRecyclerView, adapter, manager)
)

ถัดไปในคลาส FriendlyMessageAdapter.kt ให้ใช้เมธอด bind() ในคลาสภายใน MessageViewHolder():

friendlyMessageAdapter.kt

inner class MessageViewHolder(private val binding: MessageBinding) : ViewHolder(binding.root) {
    fun bind(item: FriendlyMessage) {
        binding.messageTextView.text = item.text
        setTextColor(item.name, binding.messageTextView)

        binding.messengerTextView.text = if (item.name == null) ANONYMOUS else item.name
        if (item.photoUrl != null) {
            loadImageIntoView(binding.messengerImageView, item.photoUrl!!)
        } else {
            binding.messengerImageView.setImageResource(R.drawable.ic_account_circle_black_36dp)
        }
    }
    ...
}

เรายังต้องแสดงข้อความที่เป็นรูปภาพด้วย ดังนั้นให้ใช้เมธอด bind() ในคลาสภายใน ImageMessageViewHolder() ด้วย

friendlyMessageAdapter.kt

inner class ImageMessageViewHolder(private val binding: ImageMessageBinding) :
    ViewHolder(binding.root) {
    fun bind(item: FriendlyMessage) {
        loadImageIntoView(binding.messageImageView, item.imageUrl!!)

        binding.messengerTextView.text = if (item.name == null) ANONYMOUS else item.name
        if (item.photoUrl != null) {
            loadImageIntoView(binding.messengerImageView, item.photoUrl!!)
        } else {
            binding.messengerImageView.setImageResource(R.drawable.ic_account_circle_black_36dp)
        }
    }
}

สุดท้าย ให้กลับมาที่ MainActivity จากนั้นเริ่มและหยุดฟังข้อมูลอัปเดตจากฐานข้อมูลเรียลไทม์ของ Firebase อัปเดตเมธอด onPause() และ onResume() ใน MainActivity ตามที่แสดงด้านล่าง

MainActivity.kt

public override fun onPause() {
    adapter.stopListening()
    super.onPause()
}

public override fun onResume() {
    super.onResume()
    adapter.startListening()
}

ทดสอบการซิงค์ข้อความ

  1. คลิกเรียกใช้ ( ดำเนินการ)
  2. ใน UI ของชุดโปรแกรมจำลอง ให้กลับไปที่แท็บ Realtime Database แล้วเพิ่มข้อความใหม่ด้วยตนเอง ยืนยันว่าข้อความปรากฏในแอป Android โดยทำดังนี้

ยินดีด้วย คุณเพิ่งเพิ่มฐานข้อมูลแบบเรียลไทม์ลงในแอป

8. ส่งข้อความ

ใช้การส่งข้อความ

ในส่วนนี้ คุณจะเพิ่มความสามารถสำหรับผู้ใช้แอปที่จะส่งข้อความได้ ข้อมูลโค้ดด้านล่างจะเฝ้าติดตามเหตุการณ์การคลิกบนปุ่มส่ง สร้างออบเจ็กต์ FriendlyMessage ใหม่ที่มีเนื้อหาของช่องข้อความ และพุชข้อความไปยังฐานข้อมูล เมธอด push() จะเพิ่มรหัสที่สร้างขึ้นโดยอัตโนมัติไปยังเส้นทางของออบเจ็กต์ที่พุช รหัสเหล่านี้จะเรียงตามลำดับ ซึ่งช่วยให้มั่นใจได้ว่าข้อความใหม่จะถูกเพิ่มลงในท้ายรายการ

อัปเดต Listener การคลิกของปุ่มส่งในเมธอด onCreate() ในชั้นเรียน MainActivity โค้ดนี้อยู่ที่ด้านล่างของเมธอด onCreate() แล้ว อัปเดตเนื้อหา onClick() ให้ตรงกับโค้ดด้านล่าง

MainActivity.kt

// Disable the send button when there's no text in the input field
// See MyButtonObserver for details
binding.messageEditText.addTextChangedListener(MyButtonObserver(binding.sendButton))

// When the send button is clicked, send a text message
binding.sendButton.setOnClickListener {
    val friendlyMessage = FriendlyMessage(
        binding.messageEditText.text.toString(),
        getUserName(),
        getPhotoUrl(),
        null /* no image */
    )
    db.reference.child(MESSAGES_CHILD).push().setValue(friendlyMessage)
    binding.messageEditText.setText("")
}

ใช้การส่งข้อความรูปภาพ

ในส่วนนี้ คุณจะเพิ่มความสามารถสำหรับผู้ใช้แอปที่จะส่งข้อความรูปภาพได้ สร้างข้อความพร้อมรูปภาพได้ด้วยขั้นตอนต่อไปนี้

  • เลือกรูปภาพ
  • จัดการการเลือกรูปภาพ
  • เขียนข้อความรูปภาพชั่วคราวไปยัง Realtime Database
  • เริ่มอัปโหลดรูปภาพที่เลือก
  • อัปเดต URL ข้อความรูปภาพเป็นของรูปภาพที่อัปโหลด เมื่ออัปโหลดเสร็จสมบูรณ์

เลือกรูปภาพ

Codelab นี้ใช้ Cloud Storage สำหรับ Firebase เพื่อเพิ่มรูปภาพ Cloud Storage คือพื้นที่ที่เหมาะในการจัดเก็บข้อมูลไบนารีของแอป

จัดการการเลือกรูปภาพและเขียนข้อความชั่วคราว

เมื่อผู้ใช้เลือกรูปภาพแล้ว ระบบจะเริ่มเลือกรูปภาพ Intent วิธีนี้มีการติดตั้งใช้งานในโค้ดที่ส่วนท้ายของเมธอด onCreate() แล้ว เมื่อเสร็จแล้วจะเรียกใช้เมธอด onImageSelected() ของ MainActivity เมื่อใช้ข้อมูลโค้ดด้านล่าง คุณจะเขียนข้อความพร้อม URL รูปภาพชั่วคราวไปยังฐานข้อมูลที่ระบุว่ากำลังอัปโหลดรูปภาพ

MainActivity.kt

private fun onImageSelected(uri: Uri) {
    Log.d(TAG, "Uri: $uri")
    val user = auth.currentUser
    val tempMessage = FriendlyMessage(null, getUserName(), getPhotoUrl(), LOADING_IMAGE_URL)
    db.reference
            .child(MESSAGES_CHILD)
            .push()
            .setValue(
                    tempMessage,
                    DatabaseReference.CompletionListener { databaseError, databaseReference ->
                        if (databaseError != null) {
                            Log.w(
                                    TAG, "Unable to write message to database.",
                                    databaseError.toException()
                            )
                            return@CompletionListener
                        }

                        // Build a StorageReference and then upload the file
                        val key = databaseReference.key
                        val storageReference = Firebase.storage
                                .getReference(user!!.uid)
                                .child(key!!)
                                .child(uri.lastPathSegment!!)
                        putImageInStorage(storageReference, uri, key)
                    })
}

อัปโหลดรูปภาพและอัปเดตข้อความ

เพิ่ม Method putImageInStorage() ใน MainActivity ซึ่งถูกเรียกใช้ใน onImageSelected() เพื่อเริ่มการอัปโหลดรูปภาพที่เลือก เมื่อการอัปโหลดเสร็จสมบูรณ์ คุณจะอัปเดตข้อความเพื่อใช้รูปภาพที่เหมาะสม

MainActivity.kt

private fun putImageInStorage(storageReference: StorageReference, uri: Uri, key: String?) {
    // First upload the image to Cloud Storage
    storageReference.putFile(uri)
        .addOnSuccessListener(
            this
        ) { taskSnapshot -> // After the image loads, get a public downloadUrl for the image
            // and add it to the message.
            taskSnapshot.metadata!!.reference!!.downloadUrl
                .addOnSuccessListener { uri ->
                    val friendlyMessage =
                        FriendlyMessage(null, getUserName(), getPhotoUrl(), uri.toString())
                    db.reference
                        .child(MESSAGES_CHILD)
                        .child(key!!)
                        .setValue(friendlyMessage)
                }
        }
        .addOnFailureListener(this) { e ->
            Log.w(
                TAG,
                "Image upload task was unsuccessful.",
                e
            )
        }
}

ทดสอบการส่งข้อความ

  1. ใน Android Studio ให้คลิกปุ่ม ดำเนินการเรียกใช้
  2. ป้อนข้อความที่ต้องการแล้วแตะปุ่มส่งในโปรแกรมจำลอง Android ข้อความใหม่ควรแสดงใน UI ของแอปและใน UI ของ Firebase Emulator Suite
  3. ในโปรแกรมจำลองของ Android ให้แตะเครื่องหมาย "+" รูปภาพ เพื่อเลือกรูปภาพจากอุปกรณ์ ข้อความใหม่ควรแสดงพร้อมรูปภาพตัวยึดตำแหน่งก่อน ตามด้วยรูปภาพที่เลือกเมื่ออัปโหลดรูปภาพเสร็จสมบูรณ์แล้ว ข้อความใหม่ควรปรากฏใน UI ชุดโปรแกรมจำลองด้วย โดยเฉพาะอย่างยิ่งเป็นออบเจ็กต์ในแท็บ Realtime Database และเป็น BLOB ในแท็บพื้นที่เก็บข้อมูล

9. ยินดีด้วย

คุณเพิ่งสร้างแอปพลิเคชันแชทแบบเรียลไทม์โดยใช้ Firebase

สิ่งที่คุณได้เรียนรู้

  • การตรวจสอบสิทธิ์ Firebase
  • ฐานข้อมูลเรียลไทม์ของ Firebase
  • Cloud Storage for Firebase

ต่อไป ให้ลองใช้สิ่งที่คุณได้เรียนรู้ใน Codelab นี้เพื่อเพิ่ม Firebase ไปยังแอป Android ของคุณเอง ดูข้อมูลเพิ่มเติมเกี่ยวกับ Firebase ได้ที่ firebase.google.com

หากต้องการดูวิธีตั้งค่าโปรเจ็กต์ Firebase จริง และใช้ทรัพยากร Firebase จริง (แทนโปรเจ็กต์สาธิตและเฉพาะทรัพยากรที่จำลอง) ให้ไปยังขั้นตอนถัดไป

หมายเหตุ: แม้คุณจะสร้างโครงการ Firebase จริงขึ้นมาแล้ว และโดยเฉพาะอย่างยิ่งเมื่อเริ่มสร้างแอปจริง เราขอแนะนำให้ใช้ Firebase Local Emulator Suite ในการพัฒนาและทดสอบ

10. ไม่บังคับ: สร้างและตั้งค่าโปรเจ็กต์ Firebase

ในขั้นตอนนี้ คุณจะได้สร้างโปรเจ็กต์ Firebase จริงและแอป Firebase บน Android เพื่อใช้กับ Codelab นี้ นอกจากนี้ คุณยังเพิ่มการกำหนดค่า Firebase เฉพาะแอปลงในแอปได้อีกด้วย และขั้นตอนสุดท้าย คุณจะต้องตั้งค่าทรัพยากร Firebase จริงเพื่อใช้กับแอป

สร้างโปรเจ็กต์ Firebase

  1. ไปที่คอนโซล Firebase ในเบราว์เซอร์
  2. เลือกเพิ่มโปรเจ็กต์
  3. เลือกหรือป้อนชื่อโปรเจ็กต์ คุณสามารถใช้ชื่อใดก็ได้ตามต้องการ
  4. คุณไม่จำเป็นต้องใช้ Google Analytics สำหรับ Codelab นี้ คุณจึงไม่ต้องเปิดใช้ Google Analytics สำหรับโปรเจ็กต์
  5. คลิกสร้างโปรเจ็กต์ เมื่อโปรเจ็กต์พร้อมแล้ว ให้คลิกต่อไป

เพิ่ม Firebase ลงในโปรเจ็กต์ Android

ก่อนเริ่มต้นขั้นตอนนี้ ให้รับแฮช SHA1 ของแอป เรียกใช้คำสั่งต่อไปนี้จากไดเรกทอรี build-android-start ในเครื่องเพื่อระบุ SHA1 ของคีย์การแก้ไขข้อบกพร่อง

./gradlew signingReport

Store: /Users/<username>/.android/debug.keystore
Alias: AndroidDebugKey
MD5: A5:88:41:04:8F:06:59:6A:AE:33:76:87:AA:AD:19:23
SHA1: A7:89:F5:06:A8:07:A1:22:EC:90:6A:A6:EA:C3:D4:8B:3A:30:AB:18
SHA-256: 05:A2:2A:35:EE:F2:51:23:72:4D:72:67:A5:6A:8A:58:22:2C:00:A6:AB:F6:45:D5:A1:82:D8:90:A4:69:C8:FE
Valid until: Wednesday, August 10, 2044

คุณควรจะเห็นผลลัพธ์บางส่วนเหมือนกับด้านบน บรรทัดที่สำคัญคือแฮช SHA1 หากไม่พบแฮช SHA1 โปรดดูข้อมูลเพิ่มเติมในหน้านี้

กลับไปที่คอนโซล Firebase แล้วทำตามขั้นตอนต่อไปนี้เพื่อลงทะเบียนโปรเจ็กต์ Android กับโปรเจ็กต์ Firebase

  1. จากหน้าจอภาพรวมของโปรเจ็กต์ใหม่ ให้คลิกไอคอน Android เพื่อเปิดเวิร์กโฟลว์การตั้งค่า: เพิ่มแอป Android
  2. ในหน้าจอถัดไป ให้ป้อน com.google.firebase.codelab.friendlychat เป็นชื่อแพ็กเกจของแอป
  3. คลิกลงทะเบียนแอป แล้วคลิกดาวน์โหลด google-services.json เพื่อดาวน์โหลดไฟล์การกำหนดค่า Firebase
  4. คัดลอกไฟล์ google-services.json ไปยังไดเรกทอรี app ของโปรเจ็กต์ Android
  5. ข้ามขั้นตอนถัดไปที่แสดงในเวิร์กโฟลว์การตั้งค่าของคอนโซล (ดำเนินการให้คุณแล้วในโปรเจ็กต์ build-android-start)
  6. ตรวจสอบว่าทรัพยากร Dependency ทั้งหมดพร้อมใช้งานสำหรับแอปโดยการซิงค์โปรเจ็กต์กับไฟล์ Gradle จากแถบเครื่องมือของ Android Studio ให้เลือกไฟล์ > ซิงค์โปรเจ็กต์กับไฟล์ Gradle นอกจากนี้ คุณอาจต้องเรียกใช้สร้าง/ล้างโปรเจ็กต์และสร้าง/สร้างโครงการใหม่เพื่อให้การเปลี่ยนแปลงการกำหนดค่าเกิดขึ้น

กำหนดค่าการตรวจสอบสิทธิ์ Firebase

ก่อนที่แอปจะเข้าถึง API การตรวจสอบสิทธิ์ Firebase ในนามของผู้ใช้ได้ คุณต้องเปิดใช้การตรวจสอบสิทธิ์ Firebase และผู้ให้บริการการลงชื่อเข้าใช้ที่ต้องการใช้ในแอป

  1. ในคอนโซล Firebase ให้เลือกการตรวจสอบสิทธิ์จากแผงการนำทางด้านซ้าย
  2. เลือกแท็บวิธีการลงชื่อเข้าใช้
  3. คลิกอีเมล/รหัสผ่าน แล้วสลับสวิตช์เป็นเปิดใช้ (สีน้ำเงิน)
  4. คลิก Google จากนั้นสลับสวิตช์เป็นเปิดใช้ (สีน้ำเงิน) และตั้งค่าอีเมลสนับสนุนโครงการ

หากคุณได้รับข้อผิดพลาดในภายหลังใน Codelab นี้พร้อมข้อความ "CONFIGURATION_NOT_FOUND" ให้กลับมาที่ขั้นตอนนี้แล้วตรวจสอบงานอีกครั้ง

กำหนดค่าฐานข้อมูลเรียลไทม์

แอปใน Codelab นี้จัดเก็บข้อความแชทไว้ในฐานข้อมูลเรียลไทม์ของ Firebase ในส่วนนี้ เราจะสร้างฐานข้อมูลและกำหนดค่าการรักษาความปลอดภัยผ่านภาษาการกำหนดค่า JSON ชื่อกฎการรักษาความปลอดภัยของ Firebase

  1. ในคอนโซล Firebase ให้เลือก Realtime Database จากแผงการนำทางด้านซ้าย
  2. คลิกสร้างฐานข้อมูลเพื่อสร้างอินสแตนซ์ Realtime Database ใหม่ เมื่อมีข้อความแจ้ง ให้เลือกภูมิภาค us-central1 แล้วคลิกถัดไป
  3. เมื่อมีข้อความแจ้งเกี่ยวกับกฎความปลอดภัย ให้เลือกโหมดล็อก แล้วคลิกเปิดใช้
  4. เมื่อสร้างอินสแตนซ์ฐานข้อมูลแล้ว ให้เลือกแท็บกฎ จากนั้นอัปเดตการกำหนดค่ากฎด้วยข้อมูลต่อไปนี้
     {
       "rules": {
         "messages": {
           ".read": "auth.uid != null",
           ".write": "auth.uid != null"
         }
       }
     }
    

โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีการทำงานของกฎการรักษาความปลอดภัย (รวมถึงเอกสารประกอบเกี่ยวกับตัวแปร "การตรวจสอบสิทธิ์") ในเอกสารด้านความปลอดภัยของ Realtime Database

กำหนดค่า Cloud Storage สำหรับ Firebase

  1. ในคอนโซล Firebase ให้เลือกพื้นที่เก็บข้อมูลจากแผงการนำทางด้านซ้าย
  2. คลิกเริ่มต้นใช้งานเพื่อเปิดใช้ Cloud Storage สำหรับโปรเจ็กต์
  3. ทำตามขั้นตอนในกล่องโต้ตอบเพื่อตั้งค่าที่เก็บข้อมูล โดยใช้ค่าเริ่มต้นที่แนะนำ

เชื่อมต่อกับทรัพยากร Firebase

ในขั้นตอนก่อนหน้าของ Codelab นี้ คุณเพิ่มสิ่งต่อไปนี้ลงใน MainActivity.kt แล้ว บล็อกแบบมีเงื่อนไขนี้เชื่อมต่อโปรเจ็กต์ Android ของคุณกับ Firebase Emulator Suite

// REMOVE OR DISABLE THIS
if (BuildConfig.DEBUG) {
    Firebase.database.useEmulator("10.0.2.2", 9000)
    Firebase.auth.useEmulator("10.0.2.2", 9099)
    Firebase.storage.useEmulator("10.0.2.2", 9199)
}

หากต้องการเชื่อมต่อแอปกับโปรเจ็กต์ Firebase ใหม่จริง และทรัพยากร Firebase จริงของโปรเจ็กต์ คุณจะนำการบล็อกนี้ออกหรือเรียกใช้แอปในโหมดเผยแพร่เพื่อให้ BuildConfig.DEBUG เป็น false ก็ได้