Skip to content

database-ktx: add callbackFlow for eventlisteners #4012

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Sep 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions firebase-database/ktx/api.txt
Original file line number Diff line number Diff line change
@@ -1,11 +1,57 @@
// Signature format: 2.0
package com.google.firebase.database.ktx {

public abstract sealed class ChildEvent {
}

public static final class ChildEvent.Added extends com.google.firebase.database.ktx.ChildEvent {
ctor public ChildEvent.Added(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
method @NonNull public com.google.firebase.database.DataSnapshot component1();
method @Nullable public String component2();
method @NonNull public com.google.firebase.database.ktx.ChildEvent.Added copy(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
method @Nullable public String getPreviousChildName();
method @NonNull public com.google.firebase.database.DataSnapshot getSnapshot();
property @Nullable public final String previousChildName;
property @NonNull public final com.google.firebase.database.DataSnapshot snapshot;
}

public static final class ChildEvent.Changed extends com.google.firebase.database.ktx.ChildEvent {
ctor public ChildEvent.Changed(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
method @NonNull public com.google.firebase.database.DataSnapshot component1();
method @Nullable public String component2();
method @NonNull public com.google.firebase.database.ktx.ChildEvent.Changed copy(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
method @Nullable public String getPreviousChildName();
method @NonNull public com.google.firebase.database.DataSnapshot getSnapshot();
property @Nullable public final String previousChildName;
property @NonNull public final com.google.firebase.database.DataSnapshot snapshot;
}

public static final class ChildEvent.Moved extends com.google.firebase.database.ktx.ChildEvent {
ctor public ChildEvent.Moved(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
method @NonNull public com.google.firebase.database.DataSnapshot component1();
method @Nullable public String component2();
method @NonNull public com.google.firebase.database.ktx.ChildEvent.Moved copy(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
method @Nullable public String getPreviousChildName();
method @NonNull public com.google.firebase.database.DataSnapshot getSnapshot();
property @Nullable public final String previousChildName;
property @NonNull public final com.google.firebase.database.DataSnapshot snapshot;
}

public static final class ChildEvent.Removed extends com.google.firebase.database.ktx.ChildEvent {
ctor public ChildEvent.Removed(@NonNull com.google.firebase.database.DataSnapshot snapshot);
method @NonNull public com.google.firebase.database.DataSnapshot component1();
method @NonNull public com.google.firebase.database.ktx.ChildEvent.Removed copy(@NonNull com.google.firebase.database.DataSnapshot snapshot);
method @NonNull public com.google.firebase.database.DataSnapshot getSnapshot();
property @NonNull public final com.google.firebase.database.DataSnapshot snapshot;
}

public final class DatabaseKt {
method @NonNull public static com.google.firebase.database.FirebaseDatabase database(@NonNull com.google.firebase.ktx.Firebase, @NonNull String url);
method @NonNull public static com.google.firebase.database.FirebaseDatabase database(@NonNull com.google.firebase.ktx.Firebase, @NonNull com.google.firebase.FirebaseApp app);
method @NonNull public static com.google.firebase.database.FirebaseDatabase database(@NonNull com.google.firebase.ktx.Firebase, @NonNull com.google.firebase.FirebaseApp app, @NonNull String url);
method @NonNull public static kotlinx.coroutines.flow.Flow<com.google.firebase.database.ktx.ChildEvent> getChildEvents(@NonNull com.google.firebase.database.Query);
method @NonNull public static com.google.firebase.database.FirebaseDatabase getDatabase(@NonNull com.google.firebase.ktx.Firebase);
method @NonNull public static kotlinx.coroutines.flow.Flow<com.google.firebase.database.DataSnapshot> getSnapshots(@NonNull com.google.firebase.database.Query);
method public static inline <reified T> T getValue(@NonNull com.google.firebase.database.DataSnapshot);
method public static inline <reified T> T getValue(@NonNull com.google.firebase.database.MutableData);
}
Expand Down
3 changes: 3 additions & 0 deletions firebase-database/ktx/ktx.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ plugins {
id 'kotlin-android'
}

group = "com.google.firebase"

firebaseLibrary {
releaseWith project(':firebase-database')
publishJavadoc = true
Expand Down Expand Up @@ -50,6 +52,7 @@ dependencies {
implementation project(':firebase-database')
implementation 'androidx.annotation:annotation:1.1.0'
implementation 'com.google.android.gms:play-services-tasks:18.0.1'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"

androidTestImplementation 'junit:junit:4.12'
androidTestImplementation "com.google.truth:truth:$googleTruthVersion"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.google.firebase.database.ktx

import com.google.firebase.database.DataSnapshot

/**
* Used to emit events about changes in the child locations of a given
* [Query] when using the [childEvents] Flow.
*/
sealed class ChildEvent {
/**
* Emitted when a new child is added to the location.
*
* @param snapshot An immutable snapshot of the data at the new child location
* @param previousChildName The key name of sibling location ordered before the new child. This
* will be null for the first child node of a location.
*/
data class Added(val snapshot: DataSnapshot, val previousChildName: String?) : ChildEvent()

/**
* Emitted when the data at a child location has changed.
*
* @param snapshot An immutable snapshot of the data at the new data at the child location
* @param previousChildName The key name of sibling location ordered before the child. This will
* be null for the first child node of a location.
*/
data class Changed(val snapshot: DataSnapshot, val previousChildName: String?) : ChildEvent()

/**
* Emitted when a child is removed from the location.
*
* @param snapshot An immutable snapshot of the data at the child that was removed.
*/
data class Removed(val snapshot: DataSnapshot) : ChildEvent()

/**
* Emitted when a child location's priority changes.
*
* @param snapshot An immutable snapshot of the data at the location that moved.
* @param previousChildName The key name of the sibling location ordered before the child
* location. This will be null if this location is ordered first.
*/
data class Moved(val snapshot: DataSnapshot, val previousChildName: String?) : ChildEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,20 @@ import androidx.annotation.Keep
import com.google.firebase.FirebaseApp
import com.google.firebase.components.Component
import com.google.firebase.components.ComponentRegistrar
import com.google.firebase.database.ChildEventListener
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.GenericTypeIndicator
import com.google.firebase.database.MutableData
import com.google.firebase.database.Query
import com.google.firebase.database.ValueEventListener
import com.google.firebase.ktx.Firebase
import com.google.firebase.platforminfo.LibraryVersionComponent
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.callbackFlow

/** Returns the [FirebaseDatabase] instance of the default [FirebaseApp]. */
val Firebase.database: FirebaseDatabase
Expand Down Expand Up @@ -59,6 +67,68 @@ inline fun <reified T> MutableData.getValue(): T? {
return getValue(object : GenericTypeIndicator<T>() {})
}

/**
* Starts listening to this query and emits its values via a [Flow].
*
* - When the returned flow starts being collected, a [ValueEventListener] will be attached.
* - When the flow completes, the listener will be removed.
*/
val Query.snapshots
get() = callbackFlow<DataSnapshot> {
val listener = addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
repo.scheduleNow {
trySendBlocking(snapshot)
}
}

override fun onCancelled(error: DatabaseError) {
cancel(message = "Error getting Query snapshot", cause = error.toException())
}
})
awaitClose { removeEventListener(listener) }
}

/**
* Starts listening to this query's child events and emits its values via a [Flow].
*
* - When the returned flow starts being collected, a [ChildEventListener] will be attached.
* - When the flow completes, the listener will be removed.
*/
val Query.childEvents
get() = callbackFlow<ChildEvent> {
val listener = addChildEventListener(object : ChildEventListener {
override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
repo.scheduleNow {
trySendBlocking(ChildEvent.Added(snapshot, previousChildName))
}
}

override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {
repo.scheduleNow {
trySendBlocking(ChildEvent.Changed(snapshot, previousChildName))
}
}

override fun onChildRemoved(snapshot: DataSnapshot) {
repo.scheduleNow {
trySendBlocking(ChildEvent.Removed(snapshot))
}
}

override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {
repo.scheduleNow {
trySendBlocking(ChildEvent.Moved(snapshot, previousChildName))
}
}

override fun onCancelled(error: DatabaseError) {
cancel(message = "Error getting Query childEvent", cause = error.toException())
}
})
awaitClose { removeEventListener(listener) }
}

internal const val LIBRARY_NAME: String = "fire-db-ktx"

/** @suppress */
Expand Down