Răsfoiți Sursa

kodein improvements

milan jurkulák 2 ani în urmă
părinte
comite
d2729fc275

+ 1 - 15
app/src/main/kotlin/org/mjdev/tvapp/activity/IPTVActivity.kt

@@ -8,19 +8,11 @@
 
 package org.mjdev.tvapp.activity
 
-import android.content.Context
 import android.content.Intent
 import android.net.Uri
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
-import org.kodein.di.DI
-import org.kodein.di.DIAware
-import org.kodein.di.bind
-import org.kodein.di.singleton
-import org.mjdev.tvapp.app.Application
 import org.mjdev.tvapp.data.local.Media
-import org.mjdev.tvapp.database.DAO
-import org.mjdev.tvapp.module.ViewModelsModule
 import org.mjdev.tvapp.ui.screens.GalleryScreen
 import org.mjdev.tvlib.extensions.NavGraphBuilderExt.screen
 import org.mjdev.tvlib.navigation.NavGraphBuilderEx
@@ -37,13 +29,7 @@ import org.mjdev.tvlib.navigation.NavHostControllerEx
 import java.net.URL
 
 @Suppress("PreviewShouldNotBeCalledRecursively")
-class IPTVActivity : TvActivity(), DIAware {
-
-    override val di by DI.lazy {
-        bind<Context>() with singleton { this@IPTVActivity }
-        bind<DAO>() with singleton { (applicationContext as Application).DAO }
-        import(ViewModelsModule)
-    }
+class IPTVActivity : TvActivity() {
 
     override val backgroundColor: Color = Color.Black
 

+ 1 - 15
app/src/main/kotlin/org/mjdev/tvapp/activity/MainActivity.kt

@@ -8,15 +8,7 @@
 
 package org.mjdev.tvapp.activity
 
-import android.content.Context
 import androidx.compose.runtime.Composable
-import org.kodein.di.DI
-import org.kodein.di.DIAware
-import org.kodein.di.bind
-import org.kodein.di.singleton
-import org.mjdev.tvapp.app.Application
-import org.mjdev.tvapp.database.DAO
-import org.mjdev.tvapp.module.ViewModelsModule
 import org.mjdev.tvlib.navigation.NavGraphBuilderEx
 import org.mjdev.tvapp.ui.screens.MainScreen
 import org.mjdev.tvapp.ui.screens.SplashScreen
@@ -26,13 +18,7 @@ import org.mjdev.tvlib.extensions.NavGraphBuilderExt.homeScreen
 import org.mjdev.tvlib.extensions.NavGraphBuilderExt.startScreen
 
 @Suppress("PreviewShouldNotBeCalledRecursively")
-class MainActivity : TvActivity(), DIAware {
-
-    override val di by DI.lazy {
-        bind<Context>() with singleton { this@MainActivity }
-        bind<DAO>() with singleton { (applicationContext as Application).DAO }
-        import(ViewModelsModule)
-    }
+class MainActivity : TvActivity() {
 
     @Previews
     @Composable

+ 1 - 15
app/src/main/kotlin/org/mjdev/tvapp/activity/WebActivity.kt

@@ -8,17 +8,9 @@
 
 package org.mjdev.tvapp.activity
 
-import android.content.Context
 import android.content.Intent
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
-import org.kodein.di.DI
-import org.kodein.di.DIAware
-import org.kodein.di.bind
-import org.kodein.di.singleton
-import org.mjdev.tvapp.app.Application
-import org.mjdev.tvapp.database.DAO
-import org.mjdev.tvapp.module.ViewModelsModule
 import org.mjdev.tvlib.navigation.NavGraphBuilderEx
 import org.mjdev.tvapp.ui.screens.WebScreen
 import org.mjdev.tvlib.activity.TvActivity
@@ -29,13 +21,7 @@ import org.mjdev.tvlib.extensions.StringExt.parseUri
 import org.mjdev.tvlib.navigation.NavHostControllerEx
 
 @Suppress("PreviewShouldNotBeCalledRecursively")
-class WebActivity : TvActivity(), DIAware {
-
-    override val di by DI.lazy {
-        bind<Context>() with singleton { this@WebActivity }
-        bind<DAO>() with singleton { (applicationContext as Application).DAO }
-        import(ViewModelsModule)
-    }
+class WebActivity : TvActivity() {
 
     override val backgroundColor: Color = Color.Black
 

+ 1 - 2
app/src/main/kotlin/org/mjdev/tvapp/app/Application.kt

@@ -18,10 +18,10 @@ import org.kodein.di.singleton
 import org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvapp.cast.CastService
 import org.mjdev.tvapp.database.DAO
+import org.mjdev.tvapp.module.ApplicationModule
 import org.mjdev.tvapp.sync.SyncService.Companion.createAccountAndSync
 import org.mjdev.tvlib.application.TvApplication
 import timber.log.Timber
-import org.mjdev.tvapp.module.ApplicationModule
 
 @Suppress("PropertyName")
 class Application : TvApplication(), DIAware {
@@ -30,7 +30,6 @@ class Application : TvApplication(), DIAware {
         import(androidXContextTranslators)
         bind<Application>() with singleton { applicationContext as Application }
         bind<Context>() with singleton { applicationContext }
-        bind<String>() with singleton { String() }
         import(ApplicationModule)
     }
 

+ 2 - 5
app/src/main/kotlin/org/mjdev/tvapp/module/ApplicationModule.kt

@@ -9,11 +9,8 @@
 package org.mjdev.tvapp.module
 
 import org.kodein.di.DI
-import org.kodein.di.bindSingleton
-import org.kodein.di.instance
-import org.mjdev.tvapp.BuildConfig
-import org.mjdev.tvapp.database.DAO
 
 val ApplicationModule = DI.Module("ApplicationModule") {
-    bindSingleton<DAO> { DAO(instance(), BuildConfig.DEBUG) }
+    import(MainModule)
+    import(ViewModelsModule)
 }

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/module/MainModule.kt

@@ -23,6 +23,7 @@ import org.kodein.di.bindSingleton
 import org.kodein.di.instance
 import org.kodein.di.singleton
 import org.mjdev.tvapp.BuildConfig
+import org.mjdev.tvapp.database.DAO
 import org.mjdev.tvapp.repository.ApiService
 import org.mjdev.tvlib.helpers.cursor.AudioCursor
 import org.mjdev.tvlib.helpers.cursor.PhotoCursor
@@ -39,6 +40,7 @@ val MainModule = DI.Module("MainModule") {
     val baseUrl = BuildConfig.IPTV_API_URL
 
     bind<String>() with singleton { String() }
+    bind<DAO>() with singleton { DAO(instance(), BuildConfig.DEBUG) }
 
     bindSingleton<NetworkConnectivityService> { NetworkConnectivityService(instance()) }
     bindSingleton<HttpLoggingInterceptor> {

+ 2 - 10
app/src/main/kotlin/org/mjdev/tvapp/sync/SyncService.kt

@@ -20,16 +20,11 @@ import android.os.Build
 import android.os.Bundle
 import android.os.IBinder
 import androidx.annotation.Keep
-import org.kodein.di.DI
 import org.kodein.di.DIAware
-import org.kodein.di.bind
 import org.kodein.di.instance
-import org.kodein.di.singleton
 import org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvapp.R
-import org.mjdev.tvapp.app.Application
 import org.mjdev.tvapp.database.DAO
-import org.mjdev.tvapp.module.MainModule
 import org.mjdev.tvapp.repository.ApiService
 import timber.log.Timber
 
@@ -37,11 +32,8 @@ import timber.log.Timber
 @Keep
 class SyncService : Service(), DIAware {
 
-    override val di by DI.lazy {
-        bind<Context>() with singleton { applicationContext }
-        bind<DAO>() with singleton { (applicationContext as Application).DAO }
-        import(MainModule)
-    }
+    override val di
+        get() = (applicationContext as DIAware).di
 
     val apiService: ApiService by instance()
     val dao: DAO by instance()

+ 9 - 7
app/src/main/kotlin/org/mjdev/tvapp/sync/provider/DataProvider.kt

@@ -1,5 +1,5 @@
 /*
- *  Copyright (c) Milan Jurkulák 2023.
+ *  Copyright (c) Milan Jurkulák 2024.
  *  Contact:
  *  e: mimoccc@gmail.com
  *  e: mj@mjdev.org
@@ -15,20 +15,22 @@ import android.database.Cursor
 import android.net.Uri
 import androidx.annotation.Keep
 import com.squareup.moshi.Moshi
+import org.kodein.di.DIAware
+import org.kodein.di.instance
 import org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvapp.data.local.Media
 import org.mjdev.tvapp.database.DAO
-import javax.inject.Inject
 
 @Keep
 @Suppress("MemberVisibilityCanBePrivate")
-class DataProvider : ContentProvider() {
+class DataProvider : ContentProvider(), DIAware {
 
-    @Inject
-    lateinit var dao: DAO
+    override val di
+        get() = (context?.applicationContext as DIAware).di
 
-    @Inject
-    lateinit var moshi: Moshi
+    val dao: DAO by instance()
+
+    val moshi: Moshi by instance()
 
     companion object {
 

+ 21 - 2
app/src/main/kotlin/org/mjdev/tvapp/ui/pages/MainPage.kt

@@ -9,6 +9,7 @@
 package org.mjdev.tvapp.ui.pages
 
 import android.annotation.SuppressLint
+import android.content.Context
 import android.net.Uri
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.fillMaxSize
@@ -24,12 +25,17 @@ import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
-import org.kodein.di.compose.rememberInstance
+import androidx.compose.ui.platform.LocalContext
+import org.kodein.di.DI
+import org.kodein.di.bind
+import org.kodein.di.singleton
 import org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.activity.IPTVActivity
 import org.mjdev.tvapp.activity.IPTVActivity.Companion.IPTV_DATA
 import org.mjdev.tvapp.data.events.SyncEvent
+import org.mjdev.tvapp.database.DAO
+import org.mjdev.tvapp.module.ViewModelsModule
 import org.mjdev.tvapp.sync.SyncAdapter.Companion.pauseSync
 import org.mjdev.tvlib.data.local.User
 import org.mjdev.tvlib.auth.AuthManager.Companion.rememberAuthManager
@@ -42,6 +48,7 @@ import org.mjdev.tvlib.ui.components.tv.LocalVideoRow
 import org.mjdev.tvapp.viewmodel.MainViewModel
 import org.mjdev.tvlib.annotations.Previews
 import org.mjdev.tvlib.extensions.ComposeExt.observedEvents
+import org.mjdev.tvlib.extensions.KodeinExt.rememberInstance
 import org.mjdev.tvlib.extensions.NavExt.rememberNavControllerEx
 import org.mjdev.tvlib.interfaces.ItemPhoto
 import org.mjdev.tvlib.interfaces.ItemWithBackground
@@ -52,6 +59,16 @@ import java.net.URL
 @SuppressLint("ComposableNaming")
 class MainPage : Page() {
 
+    // todo
+    @Suppress("MemberVisibilityCanBePrivate")
+    fun diMock(
+        context: Context
+    ) = DI.lazy(allowSilentOverride = true) {
+        bind<Context>() with singleton { context }
+        bind<DAO>() with singleton { DAO(context) }
+        import(ViewModelsModule)
+    }
+
     override val title: Int = R.string.title_home
     override val icon: ImageVector = Icons.Default.Home
 
@@ -60,7 +77,9 @@ class MainPage : Page() {
     @Composable
     override fun Content() {
         val navController = rememberNavControllerEx()
-        val viewModel: MainViewModel by rememberInstance()
+        val viewModel: MainViewModel by rememberInstance {
+            diMock(LocalContext.current)
+        }
         val syncEvents by observedEvents<SyncEvent>()
         // refresh every 32 items, todo improve
         val needRefresh by remember {

+ 1 - 1
app/src/main/kotlin/org/mjdev/tvapp/ui/screens/GalleryScreen.kt

@@ -18,7 +18,7 @@ import androidx.compose.runtime.remember
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.navigation.navArgument
-import org.kodein.di.compose.rememberInstance
+import org.mjdev.tvlib.extensions.KodeinExt.rememberInstance
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.sync.SyncAdapter.Companion.pauseSyncUntilGone
 import org.mjdev.tvapp.viewmodel.DetailViewModel

+ 1 - 1
app/src/main/kotlin/org/mjdev/tvapp/ui/screens/IPTVScreen.kt

@@ -19,7 +19,7 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.graphics.vector.ImageVector
 import androidx.media3.common.MediaItem
 import androidx.navigation.navArgument
-import org.kodein.di.compose.rememberInstance
+import org.mjdev.tvlib.extensions.KodeinExt.rememberInstance
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.sync.SyncAdapter.Companion.pauseSyncUntilGone
 import org.mjdev.tvapp.viewmodel.IPTVViewModel

+ 1 - 13
app/src/main/kotlin/org/mjdev/tvapp/viewmodel/DetailViewModel.kt

@@ -9,19 +9,13 @@
 package org.mjdev.tvapp.viewmodel
 
 import android.content.Context
-import org.kodein.di.DI
-import org.kodein.di.DIAware
-import org.kodein.di.bind
 import org.kodein.di.instance
-import org.kodein.di.singleton
-import org.mjdev.tvapp.app.Application
 import org.mjdev.tvapp.data.local.Media
 import org.mjdev.tvlib.helpers.cursor.AudioCursor
 import org.mjdev.tvlib.helpers.cursor.PhotoCursor
 import org.mjdev.tvlib.helpers.cursor.VideoCursor
 import org.mjdev.tvlib.viewmodel.BaseViewModel
 import org.mjdev.tvapp.database.DAO
-import org.mjdev.tvapp.module.MainModule
 import org.mjdev.tvlib.extensions.MediaItemExt.mediaItem
 import org.mjdev.tvlib.interfaces.ItemAudio
 import org.mjdev.tvlib.interfaces.ItemPhoto
@@ -30,13 +24,7 @@ import kotlin.reflect.KClass
 
 class DetailViewModel(
     context: Context
-) : BaseViewModel(context), DIAware {
-
-    override val di by DI.lazy {
-        bind<Context>() with singleton { context }
-        bind<DAO>() with singleton { (context.applicationContext as Application).DAO }
-        import(MainModule)
-    }
+) : BaseViewModel(context) {
 
     private val cache = mutableMapOf<KClass<*>, List<*>>()
 

+ 1 - 13
app/src/main/kotlin/org/mjdev/tvapp/viewmodel/IPTVViewModel.kt

@@ -12,19 +12,13 @@ package org.mjdev.tvapp.viewmodel
 
 import android.content.Context
 import androidx.media3.common.MediaItem
-import org.kodein.di.DI
-import org.kodein.di.DIAware
-import org.kodein.di.bind
 import org.kodein.di.instance
-import org.kodein.di.singleton
-import org.mjdev.tvapp.app.Application
 import org.mjdev.tvapp.data.local.Media
 import org.mjdev.tvlib.helpers.cursor.AudioCursor
 import org.mjdev.tvlib.helpers.cursor.PhotoCursor
 import org.mjdev.tvlib.helpers.cursor.VideoCursor
 import org.mjdev.tvlib.viewmodel.BaseViewModel
 import org.mjdev.tvapp.database.DAO
-import org.mjdev.tvapp.module.MainModule
 import org.mjdev.tvlib.extensions.MediaItemExt.mediaItem
 import org.mjdev.tvlib.interfaces.ItemAudio
 import org.mjdev.tvlib.interfaces.ItemPhoto
@@ -34,13 +28,7 @@ import kotlin.reflect.KClass
 
 class IPTVViewModel (
     context: Context
-) : BaseViewModel(context), DIAware {
-
-    override val di by DI.lazy {
-        bind<Context>() with singleton { context }
-        bind<DAO>() with singleton { (context.applicationContext as Application).DAO }
-        import(MainModule)
-    }
+) : BaseViewModel(context) {
 
     val dao: DAO by instance()
     val networkInfo: NetworkConnectivityService by instance()

+ 1 - 15
app/src/main/kotlin/org/mjdev/tvapp/viewmodel/MainViewModel.kt

@@ -14,33 +14,19 @@ import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.flow.channelFlow
 import kotlinx.coroutines.flow.flow
 import kotlinx.coroutines.flow.flowOn
-import org.kodein.di.DI
-import org.kodein.di.DIAware
-import org.kodein.di.bind
 import org.kodein.di.instance
-import org.kodein.di.singleton
 import org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvapp.activity.MainActivity
-import org.mjdev.tvapp.app.Application
 import org.mjdev.tvlib.helpers.cursor.AudioCursor
 import org.mjdev.tvlib.helpers.cursor.PhotoCursor
 import org.mjdev.tvlib.helpers.cursor.VideoCursor
 import org.mjdev.tvlib.viewmodel.BaseViewModel
 import org.mjdev.tvapp.database.DAO
-import org.mjdev.tvapp.module.MainModule
 import org.mjdev.tvlib.extensions.ListExt.asMap
 import org.mjdev.tvlib.extensions.ListExt.takeIf
 import org.mjdev.tvlib.interfaces.ItemWithImage.Companion.hasImage
 
-class MainViewModel(
-    context: Context
-) : BaseViewModel(context), DIAware {
-
-    override val di by DI.lazy {
-        bind<Context>() with singleton { context }
-        bind<DAO>() with singleton { (context.applicationContext as Application).DAO }
-        import(MainModule)
-    }
+class MainViewModel(context: Context) : BaseViewModel(context) {
 
     @Suppress("PrivatePropertyName")
     private val ITEMS_FROM_CATEGORY = 3

+ 2 - 1
documentation/release-notes-1.3.1.md

@@ -1,7 +1,8 @@
-## Release notes 1.0.0 - 14. 6. 2024, 9:55:33
+## Release notes 1.0.0 - 14. 6. 2024, 12:16:16
 
 * 14.06.2024
 
+* [milan jurkulák] : code cleanup 
 * [milan jurkulák] : code cleanup 
 
 * 13.06.2024

+ 5 - 1
tvlib/src/main/kotlin/org/mjdev/tvlib/activity/TvActivity.kt

@@ -27,11 +27,15 @@ import androidx.core.view.WindowCompat
 import androidx.core.view.WindowInsetsCompat
 import androidx.core.view.WindowInsetsControllerCompat.BEHAVIOR_DEFAULT
 import androidx.core.view.WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+import org.kodein.di.DIAware
 import org.mjdev.tvlib.annotations.Previews
 import timber.log.Timber
 
 @Suppress("MemberVisibilityCanBePrivate", "PreviewShouldNotBeCalledRecursively")
-open class TvActivity : ComposableActivity(), View.OnApplyWindowInsetsListener {
+open class TvActivity : ComposableActivity(), View.OnApplyWindowInsetsListener, DIAware {
+
+    override val di
+        get() = (applicationContext as DIAware).di
 
     open val backgroundColor: Color? = null
 

+ 1 - 2
tvlib/src/main/kotlin/org/mjdev/tvlib/extensions/ContextExt.kt

@@ -22,8 +22,7 @@ import java.util.Date
 object ContextExt {
 
     val Context.isEink: Boolean
-        get() =
-            Build.DEVICE == "Mudita Kompakt"
+        get() = Build.DEVICE == "Mudita Kompakt"
 
     val Context.isTV: Boolean
         get() = runCatching {

+ 73 - 0
tvlib/src/main/kotlin/org/mjdev/tvlib/extensions/KodeinExt.kt

@@ -0,0 +1,73 @@
+/*
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package org.mjdev.tvlib.extensions
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.DisallowComposableCalls
+import androidx.compose.runtime.ProvidableCompositionLocal
+import androidx.compose.runtime.compositionLocalOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.platform.LocalContext
+import org.kodein.di.DI
+import org.kodein.di.DIAware
+import org.kodein.di.LazyDelegate
+import org.kodein.di.instance
+import kotlin.reflect.KProperty
+
+object KodeinExt {
+
+    private val LocalDI: ProvidableCompositionLocal<DI?>
+        @Composable
+        get() {
+            val diAware = LocalContext.current as? DIAware
+            return compositionLocalOf {
+                diAware?.di
+            }
+        }
+
+    @Composable
+    fun localDI(
+        initializer: @Composable () -> DI? = { null }
+    ): DI = LocalDI.current ?: initializer() ?: throw (RuntimeException("No DI available."))
+
+    class ComposableDILazyDelegate<V>(
+        private val base: LazyDelegate<V>
+    ) : LazyDelegate<V> {
+        private lateinit var lazy: Lazy<V>
+
+        override fun provideDelegate(
+            receiver: Any?,
+            prop: KProperty<Any?>
+        ): Lazy<V> {
+            if (!this::lazy.isInitialized) {
+                lazy = base.provideDelegate(null, prop)
+            }
+            return lazy
+        }
+    }
+
+    @Composable
+    inline fun <reified T : Any> rememberDI(
+        noinline initializer: @Composable () -> DI? = { null },
+        crossinline block: @DisallowComposableCalls DI.() -> LazyDelegate<T>
+    ): LazyDelegate<T> = with(localDI(initializer)) {
+        remember {
+            ComposableDILazyDelegate(block())
+        }
+    }
+
+    @Composable
+    inline fun <reified T : Any> rememberInstance(
+        tag: Any? = null,
+        noinline initializer: @Composable () -> DI? = { null }
+    ): LazyDelegate<T> = rememberDI(initializer) {
+        instance(tag = tag)
+    }
+
+}

+ 6 - 1
tvlib/src/main/kotlin/org/mjdev/tvlib/viewmodel/BaseViewModel.kt

@@ -21,12 +21,17 @@ import kotlinx.coroutines.flow.StateFlow
 import kotlinx.coroutines.flow.onEach
 import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
+import org.kodein.di.DIAware
 import timber.log.Timber
 
 @Suppress("unused", "StaticFieldLeak")
 open class BaseViewModel(
     context: Context
-) : AndroidViewModel(context.applicationContext as Application) {
+) : AndroidViewModel(context.applicationContext as Application), DIAware {
+
+    override val di
+        get() = (getApplication() as DIAware).di
+
     val error: MutableStateFlow<Throwable?> = MutableStateFlow(null)
 
     private var errorHandler: (error: Throwable) -> Unit = { e -> Timber.e(e) }