Skip to content

Commit f9705f0

Browse files
committed
add stuff
1 parent af307ed commit f9705f0

File tree

18 files changed

+378
-11
lines changed

18 files changed

+378
-11
lines changed

.idea/vcs.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ dependencies {
5757
// Extensions for LiveData
5858
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0-alpha02'
5959

60+
// Java 8 support
61+
implementation "androidx.lifecycle:lifecycle-common-java8:2.3.0-alpha02"
62+
6063
// coroutines
6164
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5'
6265
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.5'

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
package="com.hoc081098.firestore_coroutinesflow">
44

5+
<uses-permission android:name="android.permission.INTERNET" />
6+
57
<application
8+
android:name=".App"
69
android:allowBackup="true"
710
android:icon="@mipmap/ic_launcher"
811
android:label="@string/app_name"
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.hoc081098.firestore_coroutinesflow
2+
3+
import android.app.Application
4+
import com.hoc081098.firestore_coroutinesflow.koin.dataModule
5+
import com.hoc081098.firestore_coroutinesflow.koin.domainModule
6+
import com.hoc081098.firestore_coroutinesflow.koin.viewModelModule
7+
import kotlinx.coroutines.ExperimentalCoroutinesApi
8+
import org.koin.android.ext.koin.androidContext
9+
import org.koin.android.ext.koin.androidLogger
10+
import org.koin.core.context.startKoin
11+
12+
@Suppress("unused")
13+
@ExperimentalCoroutinesApi
14+
class App : Application() {
15+
override fun onCreate() {
16+
super.onCreate()
17+
18+
startKoin {
19+
androidLogger()
20+
androidContext(this@App)
21+
modules(
22+
dataModule,
23+
domainModule,
24+
viewModelModule,
25+
)
26+
}
27+
}
28+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.hoc081098.firestore_coroutinesflow
2+
3+
sealed class Lce<out T> {
4+
object Loading : Lce<Nothing>()
5+
data class Content<out T>(val content: T) : Lce<T>()
6+
data class Error<out T>(val exception: Throwable) : Lce<T>()
7+
8+
companion object Factory {
9+
fun <T> content(content: T): Lce<T> = Content(content)
10+
fun <T> loading(): Lce<T> = Loading
11+
fun <T> error(exception: Throwable): Lce<T> = Error(exception)
12+
}
13+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.hoc081098.firestore_coroutinesflow
2+
3+
import android.util.Log
4+
import android.view.View
5+
import androidx.fragment.app.Fragment
6+
import androidx.lifecycle.DefaultLifecycleObserver
7+
import androidx.lifecycle.Lifecycle
8+
import androidx.lifecycle.LifecycleOwner
9+
import androidx.viewbinding.ViewBinding
10+
import kotlin.properties.ReadOnlyProperty
11+
import kotlin.reflect.KProperty
12+
13+
class ViewBindingDelegate<T : ViewBinding>(
14+
private val fragment: Fragment,
15+
private val viewBindingFactory: (View) -> T,
16+
) : ReadOnlyProperty<Fragment, T> {
17+
private var binding: T? = null
18+
19+
init {
20+
fragment.lifecycle.addObserver(object : DefaultLifecycleObserver {
21+
override fun onCreate(owner: LifecycleOwner) {
22+
fragment.viewLifecycleOwnerLiveData.observe(fragment) { viewLifecycleOwner ->
23+
Log.d("###", "$fragment::view::viewLifecycleOwnerLiveData")
24+
25+
viewLifecycleOwner?.lifecycle?.addObserver(object : DefaultLifecycleObserver {
26+
override fun onDestroy(owner: LifecycleOwner) {
27+
binding = null
28+
viewLifecycleOwner.lifecycle.removeObserver(this)
29+
Log.d("###", "$fragment::view::onDestroy")
30+
}
31+
})
32+
}
33+
}
34+
35+
override fun onDestroy(owner: LifecycleOwner) {
36+
fragment.lifecycle.removeObserver(this)
37+
}
38+
})
39+
}
40+
41+
override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
42+
binding?.let { return it }
43+
44+
if (!fragment.viewLifecycleOwner.lifecycle.currentState.isAtLeast(Lifecycle.State.INITIALIZED)) {
45+
error("Attempt to get view binding when fragment view is destroyed")
46+
}
47+
48+
return viewBindingFactory(thisRef.requireView()).also { binding = it }
49+
}
50+
}
51+
52+
fun <T : ViewBinding> Fragment.viewBinding(factory: (View) -> T) =
53+
ViewBindingDelegate<T>(this, factory)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package com.hoc081098.firestore_coroutinesflow.data
2+
3+
import android.util.Log
4+
import com.google.firebase.firestore.FirebaseFirestore
5+
import com.google.firebase.firestore.FirebaseFirestoreException
6+
import com.google.firebase.firestore.Query
7+
import com.google.firebase.firestore.QuerySnapshot
8+
import com.hoc081098.firestore_coroutinesflow.data.entity.CategoryEntity
9+
import com.hoc081098.firestore_coroutinesflow.domain.entity.Category
10+
import com.hoc081098.firestore_coroutinesflow.domain.repo.CategoryRepo
11+
import kotlinx.coroutines.CoroutineDispatcher
12+
import kotlinx.coroutines.Dispatchers
13+
import kotlinx.coroutines.ExperimentalCoroutinesApi
14+
import kotlinx.coroutines.channels.awaitClose
15+
import kotlinx.coroutines.flow.Flow
16+
import kotlinx.coroutines.flow.callbackFlow
17+
import kotlinx.coroutines.flow.flowOn
18+
import kotlinx.coroutines.flow.map
19+
20+
@ExperimentalCoroutinesApi
21+
class CategoryRepoImpl(
22+
private val firestore: FirebaseFirestore,
23+
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
24+
) : CategoryRepo {
25+
override fun watchCategories(): Flow<List<Category>> {
26+
return firestore
27+
.collection("categories")
28+
.snapshots()
29+
.map { (querySnapshot, exception) ->
30+
if (exception !== null) throw exception
31+
(querySnapshot ?: return@map emptyList())
32+
.documents
33+
.mapNotNull { it.toObject(CategoryEntity::class.java)?.toCategory(it.id) }
34+
}
35+
.flowOn(ioDispatcher)
36+
}
37+
}
38+
39+
private fun CategoryEntity.toCategory(id: String): Category {
40+
return Category(
41+
name = name,
42+
imageUrl = imageUrl,
43+
id = id
44+
)
45+
}
46+
47+
@ExperimentalCoroutinesApi
48+
fun Query.snapshots(): Flow<Pair<QuerySnapshot?, FirebaseFirestoreException?>> {
49+
return callbackFlow {
50+
val listener = addSnapshotListener { querySnapshot, exception ->
51+
offer(querySnapshot to exception)
52+
}
53+
awaitClose {
54+
listener.remove()
55+
Log.d("###", "Remove listener $listener")
56+
}
57+
}
58+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.hoc081098.firestore_coroutinesflow.data.entity
2+
3+
import com.google.firebase.firestore.PropertyName
4+
5+
data class CategoryEntity(
6+
@get:PropertyName("name")
7+
@set:PropertyName("name")
8+
var name: String,
9+
@get:PropertyName("imageUrl")
10+
@set:PropertyName("imageUrl")
11+
var imageUrl: String,
12+
) {
13+
@Suppress("unused")
14+
constructor() : this(name = "", imageUrl = "")
15+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.hoc081098.firestore_coroutinesflow.domain.entity
2+
3+
data class Category(
4+
val id: String,
5+
val name: String,
6+
val imageUrl: String,
7+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.hoc081098.firestore_coroutinesflow.domain.repo
2+
3+
import com.hoc081098.firestore_coroutinesflow.domain.entity.Category
4+
import kotlinx.coroutines.flow.Flow
5+
6+
interface CategoryRepo {
7+
fun watchCategories(): Flow<List<Category>>
8+
}

0 commit comments

Comments
 (0)