Skip to content

Commit 3872957

Browse files
authored
database-ktx: add callbackFlow for eventlisteners (#4012)
* add callbackFlow for RTDB's ValueEventListener * add callbackFlow for RTDB's ChildEventListener * delegate trySendBlocking to DefaultRunLoop * add group to ktx.gradle * update api.txt file
1 parent 4f24be3 commit 3872957

File tree

4 files changed

+162
-0
lines changed

4 files changed

+162
-0
lines changed

firebase-database/ktx/api.txt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,57 @@
11
// Signature format: 2.0
22
package com.google.firebase.database.ktx {
33

4+
public abstract sealed class ChildEvent {
5+
}
6+
7+
public static final class ChildEvent.Added extends com.google.firebase.database.ktx.ChildEvent {
8+
ctor public ChildEvent.Added(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
9+
method @NonNull public com.google.firebase.database.DataSnapshot component1();
10+
method @Nullable public String component2();
11+
method @NonNull public com.google.firebase.database.ktx.ChildEvent.Added copy(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
12+
method @Nullable public String getPreviousChildName();
13+
method @NonNull public com.google.firebase.database.DataSnapshot getSnapshot();
14+
property @Nullable public final String previousChildName;
15+
property @NonNull public final com.google.firebase.database.DataSnapshot snapshot;
16+
}
17+
18+
public static final class ChildEvent.Changed extends com.google.firebase.database.ktx.ChildEvent {
19+
ctor public ChildEvent.Changed(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
20+
method @NonNull public com.google.firebase.database.DataSnapshot component1();
21+
method @Nullable public String component2();
22+
method @NonNull public com.google.firebase.database.ktx.ChildEvent.Changed copy(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
23+
method @Nullable public String getPreviousChildName();
24+
method @NonNull public com.google.firebase.database.DataSnapshot getSnapshot();
25+
property @Nullable public final String previousChildName;
26+
property @NonNull public final com.google.firebase.database.DataSnapshot snapshot;
27+
}
28+
29+
public static final class ChildEvent.Moved extends com.google.firebase.database.ktx.ChildEvent {
30+
ctor public ChildEvent.Moved(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
31+
method @NonNull public com.google.firebase.database.DataSnapshot component1();
32+
method @Nullable public String component2();
33+
method @NonNull public com.google.firebase.database.ktx.ChildEvent.Moved copy(@NonNull com.google.firebase.database.DataSnapshot snapshot, @Nullable String previousChildName);
34+
method @Nullable public String getPreviousChildName();
35+
method @NonNull public com.google.firebase.database.DataSnapshot getSnapshot();
36+
property @Nullable public final String previousChildName;
37+
property @NonNull public final com.google.firebase.database.DataSnapshot snapshot;
38+
}
39+
40+
public static final class ChildEvent.Removed extends com.google.firebase.database.ktx.ChildEvent {
41+
ctor public ChildEvent.Removed(@NonNull com.google.firebase.database.DataSnapshot snapshot);
42+
method @NonNull public com.google.firebase.database.DataSnapshot component1();
43+
method @NonNull public com.google.firebase.database.ktx.ChildEvent.Removed copy(@NonNull com.google.firebase.database.DataSnapshot snapshot);
44+
method @NonNull public com.google.firebase.database.DataSnapshot getSnapshot();
45+
property @NonNull public final com.google.firebase.database.DataSnapshot snapshot;
46+
}
47+
448
public final class DatabaseKt {
549
method @NonNull public static com.google.firebase.database.FirebaseDatabase database(@NonNull com.google.firebase.ktx.Firebase, @NonNull String url);
650
method @NonNull public static com.google.firebase.database.FirebaseDatabase database(@NonNull com.google.firebase.ktx.Firebase, @NonNull com.google.firebase.FirebaseApp app);
751
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);
52+
method @NonNull public static kotlinx.coroutines.flow.Flow<com.google.firebase.database.ktx.ChildEvent> getChildEvents(@NonNull com.google.firebase.database.Query);
853
method @NonNull public static com.google.firebase.database.FirebaseDatabase getDatabase(@NonNull com.google.firebase.ktx.Firebase);
54+
method @NonNull public static kotlinx.coroutines.flow.Flow<com.google.firebase.database.DataSnapshot> getSnapshots(@NonNull com.google.firebase.database.Query);
955
method public static inline <reified T> T getValue(@NonNull com.google.firebase.database.DataSnapshot);
1056
method public static inline <reified T> T getValue(@NonNull com.google.firebase.database.MutableData);
1157
}

firebase-database/ktx/ktx.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ plugins {
1717
id 'kotlin-android'
1818
}
1919

20+
group = "com.google.firebase"
21+
2022
firebaseLibrary {
2123
releaseWith project(':firebase-database')
2224
publishJavadoc = true
@@ -50,6 +52,7 @@ dependencies {
5052
implementation project(':firebase-database')
5153
implementation 'androidx.annotation:annotation:1.1.0'
5254
implementation 'com.google.android.gms:play-services-tasks:18.0.1'
55+
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
5356

5457
androidTestImplementation 'junit:junit:4.12'
5558
androidTestImplementation "com.google.truth:truth:$googleTruthVersion"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.google.firebase.database.ktx
2+
3+
import com.google.firebase.database.DataSnapshot
4+
5+
/**
6+
* Used to emit events about changes in the child locations of a given
7+
* [Query] when using the [childEvents] Flow.
8+
*/
9+
sealed class ChildEvent {
10+
/**
11+
* Emitted when a new child is added to the location.
12+
*
13+
* @param snapshot An immutable snapshot of the data at the new child location
14+
* @param previousChildName The key name of sibling location ordered before the new child. This
15+
* will be null for the first child node of a location.
16+
*/
17+
data class Added(val snapshot: DataSnapshot, val previousChildName: String?) : ChildEvent()
18+
19+
/**
20+
* Emitted when the data at a child location has changed.
21+
*
22+
* @param snapshot An immutable snapshot of the data at the new data at the child location
23+
* @param previousChildName The key name of sibling location ordered before the child. This will
24+
* be null for the first child node of a location.
25+
*/
26+
data class Changed(val snapshot: DataSnapshot, val previousChildName: String?) : ChildEvent()
27+
28+
/**
29+
* Emitted when a child is removed from the location.
30+
*
31+
* @param snapshot An immutable snapshot of the data at the child that was removed.
32+
*/
33+
data class Removed(val snapshot: DataSnapshot) : ChildEvent()
34+
35+
/**
36+
* Emitted when a child location's priority changes.
37+
*
38+
* @param snapshot An immutable snapshot of the data at the location that moved.
39+
* @param previousChildName The key name of the sibling location ordered before the child
40+
* location. This will be null if this location is ordered first.
41+
*/
42+
data class Moved(val snapshot: DataSnapshot, val previousChildName: String?) : ChildEvent()
43+
}

firebase-database/ktx/src/main/kotlin/com/google/firebase/database/ktx/Database.kt

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,20 @@ import androidx.annotation.Keep
1818
import com.google.firebase.FirebaseApp
1919
import com.google.firebase.components.Component
2020
import com.google.firebase.components.ComponentRegistrar
21+
import com.google.firebase.database.ChildEventListener
2122
import com.google.firebase.database.DataSnapshot
23+
import com.google.firebase.database.DatabaseError
2224
import com.google.firebase.database.FirebaseDatabase
2325
import com.google.firebase.database.GenericTypeIndicator
2426
import com.google.firebase.database.MutableData
27+
import com.google.firebase.database.Query
28+
import com.google.firebase.database.ValueEventListener
2529
import com.google.firebase.ktx.Firebase
2630
import com.google.firebase.platforminfo.LibraryVersionComponent
31+
import kotlinx.coroutines.cancel
32+
import kotlinx.coroutines.channels.awaitClose
33+
import kotlinx.coroutines.channels.trySendBlocking
34+
import kotlinx.coroutines.flow.callbackFlow
2735

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

70+
/**
71+
* Starts listening to this query and emits its values via a [Flow].
72+
*
73+
* - When the returned flow starts being collected, a [ValueEventListener] will be attached.
74+
* - When the flow completes, the listener will be removed.
75+
*/
76+
val Query.snapshots
77+
get() = callbackFlow<DataSnapshot> {
78+
val listener = addValueEventListener(object : ValueEventListener {
79+
override fun onDataChange(snapshot: DataSnapshot) {
80+
repo.scheduleNow {
81+
trySendBlocking(snapshot)
82+
}
83+
}
84+
85+
override fun onCancelled(error: DatabaseError) {
86+
cancel(message = "Error getting Query snapshot", cause = error.toException())
87+
}
88+
})
89+
awaitClose { removeEventListener(listener) }
90+
}
91+
92+
/**
93+
* Starts listening to this query's child events and emits its values via a [Flow].
94+
*
95+
* - When the returned flow starts being collected, a [ChildEventListener] will be attached.
96+
* - When the flow completes, the listener will be removed.
97+
*/
98+
val Query.childEvents
99+
get() = callbackFlow<ChildEvent> {
100+
val listener = addChildEventListener(object : ChildEventListener {
101+
override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
102+
repo.scheduleNow {
103+
trySendBlocking(ChildEvent.Added(snapshot, previousChildName))
104+
}
105+
}
106+
107+
override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {
108+
repo.scheduleNow {
109+
trySendBlocking(ChildEvent.Changed(snapshot, previousChildName))
110+
}
111+
}
112+
113+
override fun onChildRemoved(snapshot: DataSnapshot) {
114+
repo.scheduleNow {
115+
trySendBlocking(ChildEvent.Removed(snapshot))
116+
}
117+
}
118+
119+
override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {
120+
repo.scheduleNow {
121+
trySendBlocking(ChildEvent.Moved(snapshot, previousChildName))
122+
}
123+
}
124+
125+
override fun onCancelled(error: DatabaseError) {
126+
cancel(message = "Error getting Query childEvent", cause = error.toException())
127+
}
128+
})
129+
awaitClose { removeEventListener(listener) }
130+
}
131+
62132
internal const val LIBRARY_NAME: String = "fire-db-ktx"
63133

64134
/** @suppress */

0 commit comments

Comments
 (0)