Преглед на файлове

dagger removal, kodein pre-implementation

milan jurkulák преди 2 години
родител
ревизия
cfac9be2a0
променени са 44 файла, в които са добавени 521 реда и са изтрити 765 реда
  1. 4 0
      app/build.gradle.kts
  2. 11 1
      app/proguard-rules.pro
  3. 4 2
      app/src/main/AndroidManifest.xml
  4. 20 8
      app/src/main/kotlin/org/mjdev/tvapp/activity/IPTVActivity.kt
  5. 20 8
      app/src/main/kotlin/org/mjdev/tvapp/activity/MainActivity.kt
  6. 16 3
      app/src/main/kotlin/org/mjdev/tvapp/activity/WebActivity.kt
  7. 21 3
      app/src/main/kotlin/org/mjdev/tvapp/app/Application.kt
  8. 0 2
      app/src/main/kotlin/org/mjdev/tvapp/database/DAO.kt
  9. 19 0
      app/src/main/kotlin/org/mjdev/tvapp/module/ApplicationModule.kt
  10. 84 0
      app/src/main/kotlin/org/mjdev/tvapp/module/MainModule.kt
  11. 0 154
      app/src/main/kotlin/org/mjdev/tvapp/module/ProvideModule.kt
  12. 23 0
      app/src/main/kotlin/org/mjdev/tvapp/module/ViewModelsModule.kt
  13. 17 12
      app/src/main/kotlin/org/mjdev/tvapp/sync/SyncService.kt
  14. 3 6
      app/src/main/kotlin/org/mjdev/tvapp/ui/pages/MainPage.kt
  15. 7 12
      app/src/main/kotlin/org/mjdev/tvapp/ui/screens/GalleryScreen.kt
  16. 7 13
      app/src/main/kotlin/org/mjdev/tvapp/ui/screens/IPTVScreen.kt
  17. 0 2
      app/src/main/kotlin/org/mjdev/tvapp/ui/screens/MainScreen.kt
  18. 0 1
      app/src/main/kotlin/org/mjdev/tvapp/ui/screens/SplashScreen.kt
  19. 32 44
      app/src/main/kotlin/org/mjdev/tvapp/viewmodel/DetailViewModel.kt
  20. 23 36
      app/src/main/kotlin/org/mjdev/tvapp/viewmodel/IPTVViewModel.kt
  21. 20 35
      app/src/main/kotlin/org/mjdev/tvapp/viewmodel/MainViewModel.kt
  22. 3 6
      build.gradle.kts
  23. 5 9
      buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/BuildTypeExt.kt
  24. 6 6
      buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/TaskExt.kt
  25. 10 16
      buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/AppPlugin.kt
  26. 9 16
      buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/LibPlugin.kt
  27. 1 3
      buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/config/AppConfig.kt
  28. 1 2
      buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/config/LibConfig.kt
  29. 1 1
      documentation/home.md
  30. 2 2
      documentation/release-notes-1.3.1.md
  31. 4 5
      gradle.properties
  32. 27 33
      gradle/libs.versions.toml
  33. 1 1
      readme.md
  34. 3 0
      tvlib/build.gradle.kts
  35. 3 2
      tvlib/src/main/AndroidManifest.xml
  36. 14 2
      tvlib/src/main/kotlin/org/mjdev/tvlib/activity/TvActivity.kt
  37. 0 2
      tvlib/src/main/kotlin/org/mjdev/tvlib/application/TvApplication.kt
  38. 93 91
      tvlib/src/main/kotlin/org/mjdev/tvlib/auth/AuthManager.kt
  39. 0 159
      tvlib/src/main/kotlin/org/mjdev/tvlib/extensions/HiltExt.kt
  40. 7 4
      tvlib/src/main/kotlin/org/mjdev/tvlib/viewmodel/BaseViewModel.kt
  41. 0 0
      web/install.kts
  42. 0 44
      web/install.sh
  43. 0 0
      web/uninstall.kts
  44. 0 19
      web/uninstall.sh

+ 4 - 0
app/build.gradle.kts

@@ -6,6 +6,9 @@
  *  w: https://mjdev.org
  */
 
+import org.mjdev.gradle.extensions.isAndroidStudio
+
+
 plugins {
     id("AppPlugin")
 }
@@ -13,6 +16,7 @@ plugins {
 appConfig {
     namespace = "org.mjdev.tvapp"
     description = "Smart TV android app for any android device"
+    createDocumentation = !isAndroidStudio
 
     // todo
     default {

+ 11 - 1
app/proguard-rules.pro

@@ -37,19 +37,29 @@
 -dontwarn java.beans.IntrospectionException
 -dontwarn java.beans.Introspector
 -dontwarn java.beans.PropertyDescriptor
+
 -dontwarn javax.imageio.ImageIO
 -dontwarn javax.imageio.ImageReader
 -dontwarn javax.imageio.stream.ImageInputStream
+
 -dontwarn javax.lang.model.SourceVersion
+
 -dontwarn javax.naming.InvalidNameException
 -dontwarn javax.naming.NamingException
 -dontwarn javax.naming.directory.Attribute
 -dontwarn javax.naming.directory.Attributes
 -dontwarn javax.naming.ldap.LdapName
 -dontwarn javax.naming.ldap.Rdn
+
 -dontwarn org.ietf.jgss.GSSContext
 -dontwarn org.ietf.jgss.GSSCredential
 -dontwarn org.ietf.jgss.GSSException
 -dontwarn org.ietf.jgss.GSSManager
 -dontwarn org.ietf.jgss.GSSName
--dontwarn org.ietf.jgss.Oid
+-dontwarn org.ietf.jgss.Oid
+
+-keep, allowobfuscation, allowoptimization class org.kodein.type.TypeReference
+-keep, allowobfuscation, allowoptimization class org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest
+
+-keep, allowobfuscation, allowoptimization class * extends org.kodein.type.TypeReference
+-keep, allowobfuscation, allowoptimization class * extends org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest

+ 4 - 2
app/src/main/AndroidManifest.xml

@@ -21,8 +21,10 @@
         tools:ignore="ScopedStorage" />
 
     <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
-    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
-    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
+    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"
+        tools:ignore="SelectedPhotoAccess" />
+    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"
+        tools:ignore="SelectedPhotoAccess" />
 
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE" />

+ 20 - 8
app/src/main/kotlin/org/mjdev/tvapp/activity/IPTVActivity.kt

@@ -1,19 +1,26 @@
 /*
- * Copyright (c) Milan Jurkulák 2023.
- * Contact:
- * e: mimoccc@gmail.com
- * e: mj@mjdev.org
- * w: https://mjdev.org
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
  */
 
 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 dagger.hilt.android.AndroidEntryPoint
+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
@@ -30,8 +37,13 @@ import org.mjdev.tvlib.navigation.NavHostControllerEx
 import java.net.URL
 
 @Suppress("PreviewShouldNotBeCalledRecursively")
-@AndroidEntryPoint
-class IPTVActivity : TvActivity() {
+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)
+    }
 
     override val backgroundColor: Color = Color.Black
 

+ 20 - 8
app/src/main/kotlin/org/mjdev/tvapp/activity/MainActivity.kt

@@ -1,15 +1,22 @@
 /*
- * Copyright (c) Milan Jurkulák 2023.
- * Contact:
- * e: mimoccc@gmail.com
- * e: mj@mjdev.org
- * w: https://mjdev.org
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
  */
 
 package org.mjdev.tvapp.activity
 
+import android.content.Context
 import androidx.compose.runtime.Composable
-import dagger.hilt.android.AndroidEntryPoint
+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
@@ -19,8 +26,13 @@ import org.mjdev.tvlib.extensions.NavGraphBuilderExt.homeScreen
 import org.mjdev.tvlib.extensions.NavGraphBuilderExt.startScreen
 
 @Suppress("PreviewShouldNotBeCalledRecursively")
-@AndroidEntryPoint
-class MainActivity : TvActivity() {
+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)
+    }
 
     @Previews
     @Composable

+ 16 - 3
app/src/main/kotlin/org/mjdev/tvapp/activity/WebActivity.kt

@@ -8,10 +8,17 @@
 
 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 dagger.hilt.android.AndroidEntryPoint
+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
@@ -22,8 +29,13 @@ import org.mjdev.tvlib.extensions.StringExt.parseUri
 import org.mjdev.tvlib.navigation.NavHostControllerEx
 
 @Suppress("PreviewShouldNotBeCalledRecursively")
-@AndroidEntryPoint
-class WebActivity : TvActivity() {
+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)
+    }
 
     override val backgroundColor: Color = Color.Black
 
@@ -41,6 +53,7 @@ class WebActivity : TvActivity() {
             Intent.ACTION_SEND -> intent.clipData.let {
                 it?.getItemAt(0)
             }?.text
+
             else -> null
         }
         navController.openAsTop<WebScreen>(data)

+ 21 - 3
app/src/main/kotlin/org/mjdev/tvapp/app/Application.kt

@@ -8,15 +8,33 @@
 
 package org.mjdev.tvapp.app
 
-import dagger.hilt.android.HiltAndroidApp
+import android.content.Context
+import org.kodein.di.DI
+import org.kodein.di.DIAware
+import org.kodein.di.android.x.androidXContextTranslators
+import org.kodein.di.bind
+import org.kodein.di.instance
+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.sync.SyncService.Companion.createAccountAndSync
 import org.mjdev.tvlib.application.TvApplication
 import timber.log.Timber
+import org.mjdev.tvapp.module.ApplicationModule
 
-@HiltAndroidApp
-class Application : TvApplication() {
+@Suppress("PropertyName")
+class Application : TvApplication(), DIAware {
+
+    override val di by DI.lazy {
+        import(androidXContextTranslators)
+        bind<Application>() with singleton { applicationContext as Application }
+        bind<Context>() with singleton { applicationContext }
+        bind<String>() with singleton { String() }
+        import(ApplicationModule)
+    }
+
+    val DAO : DAO by instance()
 
     override fun onCreate() {
         super.onCreate()

+ 0 - 2
app/src/main/kotlin/org/mjdev/tvapp/database/DAO.kt

@@ -9,7 +9,6 @@
 package org.mjdev.tvapp.database
 
 import android.content.Context
-import dagger.hilt.android.qualifiers.ApplicationContext
 import io.objectbox.Box
 import io.objectbox.BoxStore
 import io.objectbox.kotlin.boxFor
@@ -24,7 +23,6 @@ import org.mjdev.tvapp.data.local.TVChannel
 
 @Suppress("unused", "MemberVisibilityCanBePrivate")
 class DAO(
-    @ApplicationContext
     val context: Context,
     val isDebug: Boolean = false,
     // todo

+ 19 - 0
app/src/main/kotlin/org/mjdev/tvapp/module/ApplicationModule.kt

@@ -0,0 +1,19 @@
+/*
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+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) }
+}

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

@@ -0,0 +1,84 @@
+/*
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package org.mjdev.tvapp.module
+
+import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
+import com.skydoves.sandwich.retrofit.adapters.ApiResponseCallAdapterFactory
+import com.squareup.moshi.Moshi
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.SupervisorJob
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import org.kodein.di.DI
+import org.kodein.di.bind
+import org.kodein.di.bindProvider
+import org.kodein.di.bindSingleton
+import org.kodein.di.instance
+import org.kodein.di.singleton
+import org.mjdev.tvapp.BuildConfig
+import org.mjdev.tvapp.repository.ApiService
+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.helpers.http.CacheInterceptor
+import org.mjdev.tvlib.helpers.http.NetworkConnectionInterceptor
+import org.mjdev.tvlib.helpers.http.UserAgentInterceptor
+import org.mjdev.tvlib.network.NetworkConnectivityService
+import org.mjdev.tvlib.webscrapper.adblock.AdBlockInterceptor
+import retrofit2.Retrofit
+import retrofit2.converter.moshi.MoshiConverterFactory
+
+val MainModule = DI.Module("MainModule") {
+    val baseUrl = BuildConfig.IPTV_API_URL
+
+    bind<String>() with singleton { String() }
+
+    bindSingleton<NetworkConnectivityService> { NetworkConnectivityService(instance()) }
+    bindSingleton<HttpLoggingInterceptor> {
+        HttpLoggingInterceptor().setLevel(
+            if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY
+            else HttpLoggingInterceptor.Level.NONE
+        )
+    }
+    bindSingleton<CacheInterceptor> { CacheInterceptor() }
+    bindSingleton<AdBlockInterceptor> { AdBlockInterceptor(instance()) }
+    bindSingleton<UserAgentInterceptor> { UserAgentInterceptor(instance()) }
+    bindSingleton<NetworkConnectionInterceptor> { NetworkConnectionInterceptor(instance()) }
+    bindSingleton<Moshi> { Moshi.Builder().build() }
+    bindSingleton<Retrofit> {
+        Retrofit.Builder()
+            .baseUrl(baseUrl)
+            .client(instance<OkHttpClient>())
+            .addConverterFactory(MoshiConverterFactory.create())
+            .addCallAdapterFactory(ApiResponseCallAdapterFactory.create())
+            .addCallAdapterFactory(CoroutineCallAdapterFactory())
+            .build()
+    }
+    bindSingleton<ApiService> {
+        val retrofit: Retrofit = instance()
+        retrofit.create(ApiService::class.java)
+    }
+
+    bindProvider<CoroutineScope> { CoroutineScope(SupervisorJob() + Dispatchers.Main) }
+    bindProvider<AudioCursor> { AudioCursor(instance()) }
+    bindProvider<VideoCursor> { VideoCursor(instance()) }
+    bindProvider<PhotoCursor> { PhotoCursor(instance()) }
+    bindProvider<OkHttpClient> {
+        OkHttpClient.Builder().apply {
+            addNetworkInterceptor(instance<NetworkConnectionInterceptor>())
+            addNetworkInterceptor(instance<UserAgentInterceptor>())
+            addNetworkInterceptor(instance<AdBlockInterceptor>())
+            addNetworkInterceptor(instance<CacheInterceptor>())
+            if (BuildConfig.DEBUG) {
+                addInterceptor(instance<HttpLoggingInterceptor>())
+            }
+        }.build()
+    }
+}

+ 0 - 154
app/src/main/kotlin/org/mjdev/tvapp/module/ProvideModule.kt

@@ -1,154 +0,0 @@
-/*
- *  Copyright (c) Milan Jurkulák 2024.
- *  Contact:
- *  e: mimoccc@gmail.com
- *  e: mj@mjdev.org
- *  w: https://mjdev.org
- */
-
-@file:Suppress("PrivatePropertyName")
-
-package org.mjdev.tvapp.module
-
-import android.content.Context
-import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
-import com.skydoves.sandwich.retrofit.adapters.ApiResponseCallAdapterFactory
-import com.squareup.moshi.Moshi
-import dagger.Module
-import dagger.Provides
-import dagger.hilt.InstallIn
-import dagger.hilt.android.qualifiers.ApplicationContext
-import dagger.hilt.components.SingletonComponent
-import okhttp3.OkHttpClient
-import org.mjdev.tvapp.BuildConfig
-import org.mjdev.tvapp.repository.ApiService
-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.network.NetworkConnectivityService
-import org.mjdev.tvapp.database.DAO
-import org.mjdev.tvlib.helpers.http.NetworkConnectionInterceptor
-import org.mjdev.tvlib.helpers.http.UserAgentInterceptor
-import org.mjdev.tvlib.webscrapper.adblock.AdBlockInterceptor
-import retrofit2.Retrofit
-import retrofit2.converter.moshi.MoshiConverterFactory
-import javax.inject.Singleton
-
-@Suppress("unused")
-@Module
-@InstallIn(SingletonComponent::class)
-class ProvideModule {
-
-    private val baseUrl = BuildConfig.IPTV_API_URL
-
-    @Singleton
-    @Provides
-    fun provideDAO(
-        @ApplicationContext
-        context: Context
-    ): DAO = DAO(context, BuildConfig.DEBUG)
-
-    @Singleton
-    @Provides
-    fun providesAudioCursor(
-        @ApplicationContext
-        context: Context
-    ): AudioCursor = AudioCursor(context)
-
-    @Singleton
-    @Provides
-    fun providesVideoCursor(
-        @ApplicationContext
-        context: Context
-    ): VideoCursor = VideoCursor(context)
-
-    @Singleton
-    @Provides
-    fun providesPhotoCursor(
-        @ApplicationContext
-        context: Context
-    ): PhotoCursor = PhotoCursor(context)
-
-    @Singleton
-    @Provides
-    fun providesNetworkConnectivityService(
-        @ApplicationContext
-        context: Context
-    ): NetworkConnectivityService = NetworkConnectivityService(context)
-
-//    @Singleton
-//    @Provides
-//    fun providesHttpLoggingInterceptor() = HttpLoggingInterceptor().setLevel(
-//        if (BuildConfig.DEBUG) HttpLoggingInterceptor.Level.BODY
-//        else HttpLoggingInterceptor.Level.NONE
-//    )
-
-//    @Singleton
-//    @Provides
-//    fun providesCacheInterceptor() = CacheInterceptor()
-
-    @Singleton
-    @Provides
-    fun provideAdBlockInterceptor(
-        @ApplicationContext
-        context: Context
-    ) = AdBlockInterceptor(context)
-
-    @Singleton
-    @Provides
-    fun provideUserAgentInterceptor() = UserAgentInterceptor()
-
-    @Singleton
-    @Provides
-    fun provideNetworkConnectionInterceptor(
-        @ApplicationContext
-        context: Context
-    ) = NetworkConnectionInterceptor(context)
-
-    @Singleton
-    @Provides
-    fun providesOkHttpClient(
-        networkConnectionInterceptor: NetworkConnectionInterceptor,
-        userAgentInterceptor: UserAgentInterceptor,
-//        httpLoggingInterceptor: HttpLoggingInterceptor,
-//        cacheInterceptor: CacheInterceptor,
-//        adBlockInterceptor: AdBlockInterceptor,
-    ): OkHttpClient = OkHttpClient.Builder().apply {
-        addNetworkInterceptor(networkConnectionInterceptor)
-        addNetworkInterceptor(userAgentInterceptor)
-//        addNetworkInterceptor(adBlockInterceptor)
-//        addNetworkInterceptor(cacheInterceptor)
-//        if (BuildConfig.DEBUG) {
-//            addInterceptor(httpLoggingInterceptor)
-//        }
-    }.build()
-
-    @Singleton
-    @Provides
-    fun providesMoshi(): Moshi = Moshi.Builder().build()
-
-    @Singleton
-    @Provides
-    fun providesRetrofit(
-        okHttpClient: OkHttpClient
-    ): Retrofit = Retrofit.Builder()
-        .baseUrl(baseUrl)
-        .client(okHttpClient)
-        .addConverterFactory(MoshiConverterFactory.create())
-        .addCallAdapterFactory(ApiResponseCallAdapterFactory.create())
-        .addCallAdapterFactory(CoroutineCallAdapterFactory())
-        .build()
-
-    @Singleton
-    @Provides
-    fun providesApiService(
-        retrofit: Retrofit
-    ): ApiService = retrofit.create(ApiService::class.java)
-
-//    @Provides
-//    @Singleton
-//    fun provideAppScope(): CoroutineScope {
-//        return CoroutineScope(SupervisorJob() + Dispatchers.Main)
-//    }
-
-}

+ 23 - 0
app/src/main/kotlin/org/mjdev/tvapp/module/ViewModelsModule.kt

@@ -0,0 +1,23 @@
+/*
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package org.mjdev.tvapp.module
+
+import org.kodein.di.DI
+import org.kodein.di.bind
+import org.kodein.di.instance
+import org.kodein.di.singleton
+import org.mjdev.tvapp.viewmodel.DetailViewModel
+import org.mjdev.tvapp.viewmodel.IPTVViewModel
+import org.mjdev.tvapp.viewmodel.MainViewModel
+
+val ViewModelsModule = DI.Module("ViewModelsModule") {
+    bind<MainViewModel>() with singleton { MainViewModel(instance()) }
+    bind<DetailViewModel>() with singleton { DetailViewModel(instance()) }
+    bind<IPTVViewModel>() with singleton { IPTVViewModel(instance()) }
+}

+ 17 - 12
app/src/main/kotlin/org/mjdev/tvapp/sync/SyncService.kt

@@ -20,30 +20,37 @@ import android.os.Build
 import android.os.Bundle
 import android.os.IBinder
 import androidx.annotation.Keep
-import dagger.hilt.android.AndroidEntryPoint
+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
-import javax.inject.Inject
 
 @Suppress("MemberVisibilityCanBePrivate")
 @Keep
-@AndroidEntryPoint
-class SyncService : Service() {
+class SyncService : Service(), DIAware {
 
-    @Inject
-    lateinit var apiService: ApiService
+    override val di by DI.lazy {
+        bind<Context>() with singleton { applicationContext }
+        bind<DAO>() with singleton { (applicationContext as Application).DAO }
+        import(MainModule)
+    }
 
-    @Inject
-    lateinit var dao: DAO
+    val apiService: ApiService by instance()
+    val dao: DAO by instance()
 
     override fun onCreate() {
         super.onCreate()
         synchronized(sSyncAdapterLock) {
             sSyncAdapter = sSyncAdapter ?: SyncAdapter(
-                context = this,
+                context = applicationContext,
                 apiService = apiService,
                 dao = dao
             )
@@ -80,8 +87,6 @@ class SyncService : Service() {
                         } else {
                             printError("Can not create sync account")
                         }
-                    } else {
-//                        printError("Account already exists.")
                     }
                 }
             } catch (e: Throwable) {
@@ -100,7 +105,7 @@ class SyncService : Service() {
             Timber.e("Sync resources : ${getString(R.string.sync_auth)}")
         }
 
-        fun Context.printError(message:String) {
+        fun Context.printError(message: String) {
             printError(Exception(message))
         }
 

+ 3 - 6
app/src/main/kotlin/org/mjdev/tvapp/ui/pages/MainPage.kt

@@ -24,6 +24,7 @@ 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 org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.activity.IPTVActivity
@@ -32,7 +33,6 @@ import org.mjdev.tvapp.data.events.SyncEvent
 import org.mjdev.tvapp.sync.SyncAdapter.Companion.pauseSync
 import org.mjdev.tvlib.data.local.User
 import org.mjdev.tvlib.auth.AuthManager.Companion.rememberAuthManager
-import org.mjdev.tvlib.extensions.HiltExt.appViewModel
 import org.mjdev.tvlib.ui.components.page.Page
 import org.mjdev.tvlib.ui.components.tv.AppsRow
 import org.mjdev.tvlib.ui.components.tv.BrowseView
@@ -60,11 +60,7 @@ class MainPage : Page() {
     @Composable
     override fun Content() {
         val navController = rememberNavControllerEx()
-
-        val viewModel: MainViewModel = appViewModel { context ->
-            MainViewModel.mock(context)
-        }
-
+        val viewModel: MainViewModel by rememberInstance()
         val syncEvents by observedEvents<SyncEvent>()
         // refresh every 32 items, todo improve
         val needRefresh by remember {
@@ -112,6 +108,7 @@ class MainPage : Page() {
             }
         }
 
+        // todo move to viewmodel and events
         viewModel.handleError { error ->
             errorState.value = error
         }

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

@@ -1,9 +1,9 @@
 /*
- * Copyright (c) Milan Jurkulák 2023.
- * Contact:
- * e: mimoccc@gmail.com
- * e: mj@mjdev.org
- * w: https://mjdev.org
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
  */
 
 @file:Suppress("MoveVariableDeclarationIntoWhen")
@@ -18,11 +18,11 @@ 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.tvapp.R
 import org.mjdev.tvapp.sync.SyncAdapter.Companion.pauseSyncUntilGone
 import org.mjdev.tvapp.viewmodel.DetailViewModel
 import org.mjdev.tvlib.annotations.Previews
-import org.mjdev.tvlib.extensions.HiltExt.appViewModel
 import org.mjdev.tvlib.extensions.ListExt.indexOf
 import org.mjdev.tvlib.extensions.MediaItemExt.uri
 import org.mjdev.tvlib.navigation.AnyType
@@ -49,12 +49,8 @@ class GalleryScreen : Screen() {
     @Previews
     @Composable
     override fun Content() {
-        val viewModel: DetailViewModel = appViewModel { context ->
-            DetailViewModel.mock(context)
-        }
-
+        val viewModel: DetailViewModel by rememberInstance()
         val data: Any? = remember(args) { args[data] }
-
         val dataList = remember(data) {
             viewModel.mediaItemsFor(data)
         }
@@ -66,7 +62,6 @@ class GalleryScreen : Screen() {
             list = dataList,
             index = dataList.indexOf<Any?> { item -> item.uri == data.uri },
         )
-
     }
 
 }

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

@@ -1,9 +1,9 @@
 /*
- * Copyright (c) Milan Jurkulák 2023.
- * Contact:
- * e: mimoccc@gmail.com
- * e: mj@mjdev.org
- * w: https://mjdev.org
+ *  Copyright (c) Milan Jurkulák 2024.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
  */
 
 @file:Suppress("UnusedReceiverParameter")
@@ -19,11 +19,11 @@ 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.tvapp.R
 import org.mjdev.tvapp.sync.SyncAdapter.Companion.pauseSyncUntilGone
 import org.mjdev.tvapp.viewmodel.IPTVViewModel
 import org.mjdev.tvlib.annotations.Previews
-import org.mjdev.tvlib.extensions.HiltExt.appViewModel
 import org.mjdev.tvlib.navigation.AnyType
 import org.mjdev.tvlib.screen.Screen
 import org.mjdev.tvlib.ui.components.media.MediaPlayerContainer
@@ -53,17 +53,11 @@ class IPTVScreen : Screen() {
     @Previews
     @Composable
     override fun Content() {
-
-        val viewModel: IPTVViewModel = appViewModel { context ->
-            IPTVViewModel.mock(context)
-        }
-
+        val viewModel: IPTVViewModel by rememberInstance()
         val data: Any? = args[data]
-
         val dataList: List<MediaItem> = remember(data) {
             viewModel.mediaItemsFor(data)
         }
-
         val index = remember(data) {
             dataList.indexOf<Any?> { item -> item.uri == data.uri }
         }

+ 0 - 2
app/src/main/kotlin/org/mjdev/tvapp/ui/screens/MainScreen.kt

@@ -30,7 +30,6 @@ class MainScreen : ScreenWithPages() {
     override val immersive: Boolean = false
 
     override val pages: (PagerScope.() -> Unit) = {
-
         page(SearchPage(), menuGravity = Top)
 
         page(MainPage(), isStartPage = true)
@@ -38,7 +37,6 @@ class MainScreen : ScreenWithPages() {
         page(PluginsPage())
 
         page(AboutPage(), menuGravity = Bottom)
-
     }
 
     @Previews

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

@@ -49,7 +49,6 @@ class SplashScreen : Screen() {
     @Previews
     @Composable
     override fun Content() {
-
         val isEdit = isEditMode()
         val scale = remember(isEdit) { Animatable(if (isEdit) 1f else 0f) }
 //        val alpha = remember(isEdit) { Animatable(0.5f) }

+ 32 - 44
app/src/main/kotlin/org/mjdev/tvapp/viewmodel/DetailViewModel.kt

@@ -6,75 +6,63 @@
  *  w: https://mjdev.org
  */
 
-@file:Suppress("unused")
-
 package org.mjdev.tvapp.viewmodel
 
 import android.content.Context
-import dagger.hilt.android.lifecycle.HiltViewModel
+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
 import org.mjdev.tvlib.interfaces.ItemVideo
-import javax.inject.Inject
-
-@Suppress("MemberVisibilityCanBePrivate")
-@HiltViewModel
-class DetailViewModel @Inject constructor() : BaseViewModel() {
+import kotlin.reflect.KClass
 
-//    private val cache = mutableMapOf<KClass<*>, List<*>>()
+class DetailViewModel(
+    context: Context
+) : BaseViewModel(context), DIAware {
 
-    @Inject
-    lateinit var dao: DAO
+    override val di by DI.lazy {
+        bind<Context>() with singleton { context }
+        bind<DAO>() with singleton { (context.applicationContext as Application).DAO }
+        import(MainModule)
+    }
 
-    @Inject
-    lateinit var localAudioCursor: AudioCursor
+    private val cache = mutableMapOf<KClass<*>, List<*>>()
 
-    @Inject
-    lateinit var localVideoCursor: VideoCursor
+    val dao: DAO by instance()
 
-    @Inject
-    lateinit var localPhotoCursor: PhotoCursor
+    private val localAudioCursor: AudioCursor by instance()
+    private val localVideoCursor: VideoCursor by instance()
+    private val localPhotoCursor: PhotoCursor by instance()
 
     // todo : improve on changes
-    inline fun <reified T> getCachedList(creator: () -> List<*>): List<*> {
-//        if (!cache.containsKey(T::class)) {
-//            creator().let { list ->
-//                cache[T::class] = list
-//            }
-//        }
-//        return cache[T::class] ?: emptyList<Any>()
-        return creator()
+    private inline fun <reified T> getCachedList(creator: () -> List<T>): List<*> {
+        if (!cache.containsKey(T::class)) {
+            creator().let { list ->
+                cache[T::class] = list
+            }
+        }
+        return cache[T::class] ?: emptyList<T>()
     }
 
     fun mediaItemsFor(
         data: Any?
     ): List<Any?> = when (data) {
-        is ItemAudio -> getCachedList<ItemAudio> { localAudioCursor }
-        is ItemVideo -> getCachedList<ItemVideo> { localVideoCursor }
-        is ItemPhoto -> getCachedList<ItemPhoto> { localPhotoCursor }
-        is Media -> getCachedList<Media> { dao.allMediaItems }
+        is ItemAudio -> getCachedList { localAudioCursor }
+        is ItemVideo -> getCachedList { localVideoCursor }
+        is ItemPhoto -> getCachedList { localPhotoCursor }
+        is Media -> getCachedList { dao.allMediaItems }
         else -> listOf(data.mediaItem)
     }
-
-    companion object {
-
-        @Suppress("unused")
-        fun mock(
-            context: Context
-        ): DetailViewModel = DetailViewModel().apply {
-            dao = DAO(context, true)
-            localAudioCursor = AudioCursor(context)
-            localVideoCursor = VideoCursor(context)
-            localPhotoCursor = PhotoCursor(context)
-        }
-
-    }
-
 }

+ 23 - 36
app/src/main/kotlin/org/mjdev/tvapp/viewmodel/IPTVViewModel.kt

@@ -12,43 +12,45 @@ package org.mjdev.tvapp.viewmodel
 
 import android.content.Context
 import androidx.media3.common.MediaItem
-import dagger.hilt.android.lifecycle.HiltViewModel
+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
 import org.mjdev.tvlib.interfaces.ItemVideo
 import org.mjdev.tvlib.network.NetworkConnectivityService
-import javax.inject.Inject
 import kotlin.reflect.KClass
 
-@Suppress("MemberVisibilityCanBePrivate")
-@HiltViewModel
-class IPTVViewModel @Inject constructor() : BaseViewModel() {
+class IPTVViewModel (
+    context: Context
+) : BaseViewModel(context), DIAware {
 
-    @Inject
-    lateinit var dao: DAO
-
-    @Inject
-    lateinit var networkInfo: NetworkConnectivityService
-
-    @Inject
-    lateinit var localAudioCursor: AudioCursor
-
-    @Inject
-    lateinit var localVideoCursor: VideoCursor
+    override val di by DI.lazy {
+        bind<Context>() with singleton { context }
+        bind<DAO>() with singleton { (context.applicationContext as Application).DAO }
+        import(MainModule)
+    }
 
-    @Inject
-    lateinit var localPhotoCursor: PhotoCursor
+    val dao: DAO by instance()
+    val networkInfo: NetworkConnectivityService by instance()
 
-    val mediaItems: List<Any> get() = dao.movieDao.all
+    private val localAudioCursor: AudioCursor by instance()
+    private val localVideoCursor: VideoCursor by instance()
+    private val localPhotoCursor: PhotoCursor by instance()
 
-    val cache = mutableMapOf<KClass<*>, List<MediaItem>>()
+    private val mediaItems: List<Any> get() = dao.movieDao.all
+    private val cache = mutableMapOf<KClass<*>, List<MediaItem>>()
 
     fun mediaItemsFor(
         data: Any?
@@ -61,6 +63,7 @@ class IPTVViewModel @Inject constructor() : BaseViewModel() {
                 (listItem as? Media?)?.category == data.category
             }
         }
+
         else -> listOf(data.mediaItem)
     }
 
@@ -72,20 +75,4 @@ class IPTVViewModel @Inject constructor() : BaseViewModel() {
         }
         return cache[T::class] ?: emptyList()
     }
-
-    companion object {
-
-        @Suppress("unused")
-        fun mock(
-            context: Context
-        ): IPTVViewModel = IPTVViewModel().apply {
-            dao = DAO(context, true)
-            networkInfo = NetworkConnectivityService(context)
-            localAudioCursor = AudioCursor(context)
-            localVideoCursor = VideoCursor(context)
-            localPhotoCursor = PhotoCursor(context)
-        }
-
-    }
-
 }

+ 20 - 35
app/src/main/kotlin/org/mjdev/tvapp/viewmodel/MainViewModel.kt

@@ -10,26 +10,37 @@ package org.mjdev.tvapp.viewmodel
 
 import android.content.ComponentName
 import android.content.Context
-import dagger.hilt.android.lifecycle.HiltViewModel
-import dagger.hilt.android.qualifiers.ApplicationContext
 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
-import javax.inject.Inject
 
-@HiltViewModel
-class MainViewModel @Inject constructor() : BaseViewModel() {
+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)
+    }
 
     @Suppress("PrivatePropertyName")
     private val ITEMS_FROM_CATEGORY = 3
@@ -39,22 +50,10 @@ class MainViewModel @Inject constructor() : BaseViewModel() {
         MainActivity::class.java.name
     )
 
-    @ApplicationContext
-    @Inject
-    lateinit var context: Context
-
-    @Inject
-    lateinit var dao: DAO
-
-    @Inject
-    lateinit var localAudioCursor: AudioCursor
-
-    @Inject
-    lateinit var localVideoCursor: VideoCursor
-
-    @Inject
-    lateinit var localPhotoCursor: PhotoCursor
-
+    val dao: DAO by instance()
+    val localAudioCursor: AudioCursor by instance()
+    val localVideoCursor: VideoCursor by instance()
+    val localPhotoCursor: PhotoCursor by instance()
 //    // todo : move to cursor
 //    val apps
 //        get() = appsManager(context, excludedActivity).stateInViewModel()
@@ -99,18 +98,4 @@ class MainViewModel @Inject constructor() : BaseViewModel() {
             emit(dao.countriesDao.all)
         }.flowOn(Dispatchers.IO).stateInViewModel()
 
-    companion object {
-
-        @Suppress("unused")
-        fun mock(
-            context: Context
-        ): MainViewModel = MainViewModel().apply {
-            dao = DAO(context, true)
-            localAudioCursor = AudioCursor(context)
-            localVideoCursor = VideoCursor(context)
-            localPhotoCursor = PhotoCursor(context)
-        }
-
-    }
-
 }

+ 3 - 6
build.gradle.kts

@@ -7,12 +7,10 @@
  */
 
 plugins {
-    alias(libs.plugins.gradle.android.dagger.hilt) apply false
     alias(libs.plugins.gradle.devtools.google.ksp) apply false
     alias(libs.plugins.gradle.ktlint) apply false
     alias(libs.plugins.gradle.versions) apply true
     alias(libs.plugins.gradle.catalogs.update) apply true
-    //alias(libs.plugins.gradle.node) apply true
 }
 
 buildscript {
@@ -35,7 +33,6 @@ buildscript {
         classpath(libs.gradle.dokka.plugin)
         classpath(libs.gradle.markdown.plugin)
         classpath(libs.gradle.ospackage.plugin)
-        //classpath(libs.gradle.node.plugin)
     }
 
     configurations.classpath {
@@ -56,9 +53,9 @@ versionCatalogUpdate {
 //        libraries = [libs.my.library.reference, libs.my.other.library.reference]
 //        plugins = [libs.plugins.my.plugin, libs.plugins.my.other.plugin]
 //        groups = ["com.somegroup", "com.someothergroup"]
-        keepUnusedVersions = true
-        keepUnusedLibraries = true
-        keepUnusedPlugins = true
+        keepUnusedVersions = false
+        keepUnusedLibraries = false
+        keepUnusedPlugins = false
     }
 }
 

+ 5 - 9
buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/BuildTypeExt.kt

@@ -44,7 +44,7 @@ fun ApplicationVariantDimension.stringRes(
 }
 
 fun ApplicationVariantDimension.addSyncProviderAuthString(
-    applicationId:String,
+    applicationId: String,
     name: String = "sync_auth",
     suffix: String = "sync"
 ) {
@@ -53,7 +53,7 @@ fun ApplicationVariantDimension.addSyncProviderAuthString(
 }
 
 fun ApplicationVariantDimension.stringResWithPackageName(
-    applicationId:String,
+    applicationId: String,
     vararg fields: Pair<String, String>
 ) {
     fields.forEach { pair ->
@@ -74,11 +74,7 @@ fun ApplicationVariantDimension.buildConfigStringWithPackageName(
 }
 
 fun ApplicationVariantDimension.manifestPlaceholders(
-    vararg fields: Pair<String, Any>
+    vararg placeholders: Pair<String, Any>
 ) {
-    manifestPlaceholders.apply {
-        fields.forEach { pair ->
-            put(pair.first, pair.second)
-        }
-    }
-}
+    placeholders.forEach { pair -> manifestPlaceholders.put(pair.first, pair.second) }
+}

+ 6 - 6
buildSrc/src/main/kotlin/org/mjdev/gradle/extensions/TaskExt.kt

@@ -125,12 +125,12 @@ inline fun <reified T : Task> Task.registerTask(
 fun Task.files(vararg paths: Any): ConfigurableFileCollection =
     project.files(paths)
 
-@Suppress("UnstableApiUsage")
-fun Task.finalizeDsl(block: CommonExtension<*, *, *, *, *>.() -> Unit) {
-    androidComponents.finalizeDsl { android ->
-        block(android)
-    }
-}
+//@Suppress("UnstableApiUsage")
+//fun Task.finalizeDsl(block: CommonExtension<*, *, *, *, *>.() -> Unit) {
+//    androidComponents.finalizeDsl { android ->
+//        block(android)
+//    }
+//}
 
 //fun Task.registerSourceDirectory(
 //    dir: File

+ 10 - 16
buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/AppPlugin.kt

@@ -56,6 +56,7 @@ import org.mjdev.gradle.tasks.CleanProjectTask
 import org.kordamp.gradle.plugin.markdown.MarkdownPlugin
 import org.mjdev.gradle.extensions.assembleTasks
 import org.mjdev.gradle.extensions.cleanProjectTask
+import org.mjdev.gradle.extensions.isAndroidStudio
 import org.mjdev.gradle.extensions.markDownToHtmlTask
 import org.mjdev.gradle.extensions.variants
 import org.mjdev.gradle.extensions.zipReleaseCreateTask
@@ -71,8 +72,6 @@ class AppPlugin : BasePlugin() {
         applyPlugin(libs.plugins.kotlin.android)
         applyPlugin(libs.plugins.kotlin.parcelize)
         applyPlugin(libs.plugins.google.devtools.ksp)
-        applyPlugin(libs.plugins.google.dagger.hilt.android)
-        applyPlugin(libs.plugins.dagger.hilt.android)
         applyPlugin(libs.plugins.objectbox)
         applyPlugin(libs.plugins.gradle.dokka)
         applyPlugin(libs.plugins.kotlin.compose.compiler)
@@ -332,21 +331,16 @@ class AppPlugin : BasePlugin() {
             debugImplementation(libs.androidx.customview.poolingcontainer)
             // foundation
             implementation(libs.androidx.foundation)
-            // dagger
-            implementation(libs.dagger)
-            // dagger android
-            implementation(libs.dagger.android)
-            implementation(libs.dagger.android.support)
-            // dagger hilt
-            implementation(libs.dagger.hilt.android)
-            implementation(libs.androidx.compose.hilt.navigation)
-            // dagger annotations
-            ksp(libs.dagger.compiler)
-            ksp(libs.dagger.android.processor)
-            ksp(libs.dagger.hilt.compiler)
+            // moshi
             implementation(libs.moshi)
             implementation(libs.moshi.retrofit.converter)
             ksp(libs.moshi.kotlin.codegen)
+            // Kodein DI
+            implementation(libs.kodein.di)
+            implementation(libs.kodein.di.framework.compose)
+            implementation(libs.kodein.di.framework.android.x)
+            implementation(libs.kodein.di.framework.android.x.viewmodel)
+            implementation(libs.kodein.di.framework.android.x.viewmodel.savedstate)
             // okhttp
             implementation(platform(libs.okhttp3.bom))
             implementation(libs.okhttp3)
@@ -420,8 +414,8 @@ class AppPlugin : BasePlugin() {
             // anr
             implementation(libs.anrwatchdog)
             // oauth
-            implementation(libs.auth0)
-            implementation(libs.android.jwtdecode)
+//            implementation(libs.auth0)
+//            implementation(libs.android.jwtdecode)
         }
     }
 }

+ 9 - 16
buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/LibPlugin.kt

@@ -50,8 +50,6 @@ class LibPlugin : BasePlugin() {
         applyPlugin(libs.plugins.kotlin.parcelize)
         applyPlugin(libs.plugins.google.devtools.ksp)
         applyPlugin(libs.plugins.google.devtools.ksp)
-        applyPlugin(libs.plugins.google.dagger.hilt.android)
-        applyPlugin(libs.plugins.dagger.hilt.android)
         applyPlugin(libs.plugins.objectbox)
         applyPlugin(libs.plugins.gradle.dokka)
         applyPlugin(libs.plugins.kotlin.compose.compiler)
@@ -197,17 +195,12 @@ class LibPlugin : BasePlugin() {
             debugImplementation(libs.androidx.customview.poolingcontainer)
             // foundation
             implementation(libs.androidx.foundation)
-            // dagger
-            implementation(libs.dagger)
-            // dagger android
-            implementation(libs.dagger.android)
-            implementation(libs.dagger.android.support)
-            // dagger hilt
-            implementation(libs.dagger.hilt.android)
-            implementation(libs.androidx.compose.hilt.navigation)
-            ksp(libs.dagger.compiler)
-            ksp(libs.dagger.android.processor)
-            ksp(libs.dagger.hilt.compiler)
+            // Kodein DI
+            implementation(libs.kodein.di)
+            implementation(libs.kodein.di.framework.compose)
+            implementation(libs.kodein.di.framework.android.x)
+            implementation(libs.kodein.di.framework.android.x.viewmodel)
+            implementation(libs.kodein.di.framework.android.x.viewmodel.savedstate)
             // moshi
             implementation(libs.moshi)
             implementation(libs.moshi.retrofit.converter)
@@ -263,7 +256,7 @@ class LibPlugin : BasePlugin() {
             implementation(libs.zxing.core)
             // svg
             implementation(libs.androidsvg.aar)
-            // pallette
+            // palette
             implementation(libs.androidx.palette.ktx)
             // lottie
             implementation(libs.compose.lottie)
@@ -284,8 +277,8 @@ class LibPlugin : BasePlugin() {
             // anr
             implementation(libs.anrwatchdog)
             // oauth
-            implementation(libs.auth0)
-            implementation(libs.android.jwtdecode)
+//            implementation(libs.auth0)
+//            implementation(libs.android.jwtdecode)
         }
     }
 }

+ 1 - 3
buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/config/AppConfig.kt

@@ -14,7 +14,6 @@ import org.mjdev.gradle.plugin.config.base.BuildConfigs
 
 @Suppress("unused")
 open class AppConfig : BuildConfigs() {
-
     open var namespace = "org.mjdev.tvapp"
     open var description = ""
 
@@ -74,5 +73,4 @@ open class AppConfig : BuildConfigs() {
     }
 
     override fun toMap(): Map<*, *> = toHashMap()
-
-}
+}

+ 1 - 2
buildSrc/src/main/kotlin/org/mjdev/gradle/plugin/config/LibConfig.kt

@@ -60,5 +60,4 @@ open class LibConfig : BuildConfigs() {
     }
 
     override fun toMap(): Map<*, *> = toHashMap()
-
-}
+}

+ 1 - 1
documentation/home.md

@@ -39,7 +39,7 @@ Documentation contains all the classes in project.
 Just an test app to skills showcase of coding skills, using
 
 - Compose
-- Dagger/Hilt
+- Kodein inject
 - ViewModels MVVM pattern
 - Glide
 - Kotlin

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

@@ -1,8 +1,8 @@
-## Release notes 1.0.0 - 22. 5. 2024, 1:09:41
+## Release notes 1.0.0 - 13. 6. 2024, 10:34:19
 
 * 22.05.2024
 
-* [milan jurkulák] : update libs 
+* [milan jurkulák] : update libs  update libs 
 
 * 20.05.2024
 

+ 4 - 5
gradle.properties

@@ -5,8 +5,7 @@
 #  e: mj@mjdev.org
 #  w: https://mjdev.org
 #
-
-org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
+org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8
 android.useAndroidX=true
 kotlin.code.style=official
 android.nonTransitiveRClass=true
@@ -16,11 +15,11 @@ android.nonFinalResIds=true
 gradle.testLauncherAPI.enabled=false
 kotlin.mpp.androidSourceSetLayoutVersion=2
 kotlin.mpp.applyDefaultHierarchyTemplate=false
-#android.experimental.lint.version=8.3.0-alpha18
-#org.gradle.warning.mode=all
 createReports=true
 systemProp.javax.xml.accessExternalDTD=http
 systemProp.javax.xml.parsers.SAXParserFactory=com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl
-#kotlin.experimental.tryK2=true
 kapt.use.worker.api=false
 kapt.incremental.apt=false
+android.enableJetifier=true
+#org.gradle.warning.mode=all
+#kotlin.experimental.tryK2=true

+ 27 - 33
gradle/libs.versions.toml

@@ -13,24 +13,22 @@ androidx-espresso-core = "3.5.1"
 androidx-exifinterface = "1.3.7"
 androidx-foundation = "1.6.7"
 androidx-glance-appwidget = "1.0.0"
-androidx-hilt-navigation-compose = "1.2.0"
 androidx-junit = "1.1.5"
 androidx-ktx = "1.13.1"
-androidx-lifecycle-viewmodel-compose = "2.8.0"
+androidx-lifecycle-viewmodel-compose = "2.8.1"
 androidx-material-icons-extended = "1.6.7"
 androidx-material3 = "1.2.1"
 androidx-media3 = "1.3.1"
 androidx-navigation-compose = "2.7.7"
 androidx-palette-ktx = "1.0.0"
 androidx-tv = "1.0.0-alpha10"
-androidx-window = "1.2.0"
+androidx-window = "1.3.0"
 androidx-work-runtime-ktx = "2.9.0"
 anrwatchdog = "1.4.0"
-apk-parser = "2.6.10"
-auth0 = "2.10.2"
+#apk-parser = "2.6.10"
+#auth0 = "2.10.2"
 compose = "4.14.0"
 converter-moshi = "2.11.0"
-dagger = "2.51.1"
 dailymotion-sdk-android = "0.2.12"
 #
 dependency-analysis-gradle-plugin = "0.10.0"
@@ -38,8 +36,7 @@ dependency-analysis-gradle-plugin = "0.10.0"
 detekt-gradle-plugin = "1.23.6"
 dokka-gradle-plugin = "1.9.20"
 google-api-services-androidpublisher = "v3-rev41-1.25.0"
-gradle = "8.2.2"
-gradle-api = "8.2.2"
+gradle = "8.4.2"
 gradle-docker-plugin = "9.4.0"
 #gradleNodePlugin = "1.3.1"
 gradleOspackagePlugin = "9.1.1"
@@ -48,7 +45,7 @@ ini4j = "0.5.4"
 javapoet = "1.13.0"
 jsoup = "1.17.2"
 junit = "4.13.2"
-jwtdecode = "2.0.2"
+#jwtdecode = "2.0.2"
 korim-jvm = "4.0.2"
 kotlin = "2.0.0-RC3"
 kotlinpoet = "1.16.0"
@@ -74,20 +71,24 @@ retrofit2-kotlin-coroutines-adapter = "0.9.2"
 sandwich = "2.0.7"
 timber = "5.0.1"
 zxing-core = "3.5.3"
+kodein-di = "7.22.0"
+gradle-catalogs-update = "0.8.4"
+gradle-versions = "0.51.0"
+ktlint = "11.3.2"
+gradle-devtools-google-ksp = "2.0.0-RC3-1.0.20"
 
 # libraries ########################################################################################
 [libraries]
 # common
 accompanist-permissions = { module = "com.google.accompanist:accompanist-permissions", version.ref = "accompanist-permissions" }
 aescrypt = { module = "com.scottyab:aescrypt", version.ref = "aescrypt" }
-android-jwtdecode = { module = "com.auth0.android:jwtdecode", version.ref = "jwtdecode" }
+#android-jwtdecode = { module = "com.auth0.android:jwtdecode", version.ref = "jwtdecode" }
 # material
 android-material = { module = "com.google.android.material:material", version.ref = "android-material" }
 androidsvg-aar = { module = "com.caverock:androidsvg-aar", version.ref = "androidsvg-aar" }
 androidx-compose-activity = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity-compose" }
 # compose
 androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "androidx-compose-bom" }
-androidx-compose-hilt-navigation = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "androidx-hilt-navigation-compose" }
 androidx-compose-lifecycle-viewmodel = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle-viewmodel-compose" }
 androidx-compose-navigation = { module = "androidx.navigation:navigation-compose", version.ref = "androidx-navigation-compose" }
 androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" }
@@ -132,17 +133,15 @@ androidx-tv-material = { module = "androidx.tv:tv-material", version.ref = "andr
 androidx-window = { module = "androidx.window:window", version.ref = "androidx-window" }
 androidx-work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "androidx-work-runtime-ktx" }
 anrwatchdog = { module = "com.github.anrwatchdog:anrwatchdog", version.ref = "anrwatchdog" }
-apk-parser = { module = "net.dongliu:apk-parser", version.ref = "apk-parser" }
-auth0 = { module = "com.auth0.android:auth0", version.ref = "auth0" }
+#apk-parser = { module = "net.dongliu:apk-parser", version.ref = "apk-parser" }
+#auth0 = { module = "com.auth0.android:auth0", version.ref = "auth0" }
 compose-lottie = { module = "com.airbnb.android:lottie-compose", version.ref = "lottie-compose" }
-# dagger
-dagger = { module = "com.google.dagger:dagger", version.ref = "dagger" }
-dagger-android = { module = "com.google.dagger:dagger-android", version.ref = "dagger" }
-dagger-android-processor = { module = "com.google.dagger:dagger-android-processor", version.ref = "dagger" }
-dagger-android-support = { module = "com.google.dagger:dagger-android-support", version.ref = "dagger" }
-dagger-compiler = { module = "com.google.dagger:dagger-compiler", version.ref = "dagger" }
-dagger-hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "dagger" }
-dagger-hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "dagger" }
+# kodein
+kodein-di = { module = "org.kodein.di:kodein-di", version.ref = "kodein-di" }
+kodein-di-framework-compose = { module = "org.kodein.di:kodein-di-framework-compose", version.ref = "kodein-di" }
+kodein-di-framework-android-x = { module = "org.kodein.di:kodein-di-framework-android-x", version.ref = "kodein-di" }
+kodein-di-framework-android-x-viewmodel = { module = "org.kodein.di:kodein-di-framework-android-x-viewmodel", version.ref = "kodein-di" }
+kodein-di-framework-android-x-viewmodel-savedstate = { module = "org.kodein.di:kodein-di-framework-android-x-viewmodel-savedstate", version.ref = "kodein-di" }
 # dailymotion
 dailymotion-sdk-android = { module = "com.dailymotion.dailymotion-sdk-android:sdk", version.ref = "dailymotion-sdk-android" }
 # git
@@ -154,7 +153,7 @@ glide-okhttp3-integration = { module = "com.github.bumptech.glide:okhttp3-integr
 google-api-services-androidpublisher = { module = "com.google.apis:google-api-services-androidpublisher", version.ref = "google-api-services-androidpublisher" }
 # gradle
 gradle = { module = "com.android.tools.build:gradle", version.ref = "gradle" }
-gradle-api = { module = "com.android.tools.build:gradle-api", version.ref = "gradle-api" }
+gradle-api = { module = "com.android.tools.build:gradle-api", version.ref = "gradle" }
 gradle-detekt-plugin = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt-gradle-plugin" }
 gradle-docker-plugin = { module = "com.bmuschko:gradle-docker-plugin", version.ref = "gradle-docker-plugin" }
 gradle-dokka-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka-gradle-plugin" }
@@ -214,23 +213,18 @@ kotlin-compose-compiler = { module = "org.jetbrains.kotlin:compose-compiler-grad
 
 ## plugins #########################################################################################
 [plugins]
-android-application = "com.android.application:8.4.1"
-android-library = "com.android.library:8.4.0"
-gradle-android-dagger-hilt = "com.google.dagger.hilt.android:2.47"
-gradle-catalogs-update = "nl.littlerobots.version-catalog-update:0.8.4"
-gradle-devtools-google-ksp = "com.google.devtools.ksp:2.0.0-RC3-1.0.20"
-gradle-ktlint = "org.jlleitschuh.gradle.ktlint:11.3.2"
+android-application = { id = "com.android.application", version.ref = "gradle" }
+android-library = { id = "com.android.library", version.ref = "gradle" }
+gradle-catalogs-update = { id = "nl.littlerobots.version-catalog-update", version.ref = "gradle-catalogs-update" }
+gradle-devtools-google-ksp = { id = "com.google.devtools.ksp", version.ref = "gradle-devtools-google-ksp" }
+gradle-ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
 # todo ktorfit https://foso.github.io/Ktorfit/
 #gradle-ktorfit = "de.jensklingenberg.ktorfit:1.10.2"
 #gradle-markdown = "org.kordamp.gradle.markdown:2.2.0"
-#gradle-os-packages = "nebula.ospackage:9.1.1"
-gradle-versions = { id = "com.github.ben-manes.versions", version = "0.51.0" }
-#gradle-node = { id ="com.moowork.node", version="1.3.1" }
+gradle-versions = { id = "com.github.ben-manes.versions", version.ref = "gradle-versions" }
 kotlin-compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
 kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
 kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
 gradle-dokka = { id = "org.jetbrains.dokka" }
 objectbox = { id = "io.objectbox" }
 google-devtools-ksp = { id = "com.google.devtools.ksp" }
-google-dagger-hilt-android = { id = "com.google.dagger.hilt.android" }
-dagger-hilt-android = { id = "dagger.hilt.android.plugin" }

+ 1 - 1
readme.md

@@ -59,7 +59,7 @@ Components should be responsive to display size.
 Technologies that will be used:
 
 - Compose
-- Dagger/Hilt
+- Kodein inject
 - ViewModels MVVM pattern
 - Glide
 - Kotlin

+ 3 - 0
tvlib/build.gradle.kts

@@ -6,6 +6,8 @@
  *  w: https://mjdev.org
  */
 
+import org.mjdev.gradle.extensions.isAndroidStudio
+
 plugins {
     id("LibPlugin")
 }
@@ -13,4 +15,5 @@ plugins {
 libConfig {
     namespace = "org.mjdev.tvlib"
     description = "Smart TV android app lib for android applications running on any android device"
+    createDocumentation = !isAndroidStudio
 }

+ 3 - 2
tvlib/src/main/AndroidManifest.xml

@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="utf-8"?><!--
-  ~ Copyright (c) Milan Jurkulák 2023.
+  ~  Copyright (c) Milan Jurkulák 2024.
   ~  Contact:
   ~  e: mimoccc@gmail.com
   ~  e: mj@mjdev.org
@@ -20,7 +20,8 @@
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
 
-    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
+        tools:ignore="ScopedStorage" />
     <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
 
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

+ 14 - 2
tvlib/src/main/kotlin/org/mjdev/tvlib/activity/TvActivity.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
@@ -18,6 +18,8 @@ import android.view.View
 import android.view.WindowInsets
 import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
 import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
+import androidx.activity.SystemBarStyle
+import androidx.activity.enableEdgeToEdge
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.toArgb
@@ -31,7 +33,7 @@ import timber.log.Timber
 @Suppress("MemberVisibilityCanBePrivate", "PreviewShouldNotBeCalledRecursively")
 open class TvActivity : ComposableActivity(), View.OnApplyWindowInsetsListener {
 
-    open val backgroundColor : Color? = null
+    open val backgroundColor: Color? = null
 
     open var background: Drawable? = null
         get() = field ?: backgroundColor?.let { ColorDrawable(it.toArgb()) }
@@ -58,6 +60,16 @@ open class TvActivity : ComposableActivity(), View.OnApplyWindowInsetsListener {
                 ((configuration?.screenWidthDp ?: 0) > (configuration?.screenHeightDp ?: 0))
 
     override fun onCreate(savedInstanceState: Bundle?) {
+        enableEdgeToEdge(
+            statusBarStyle = SystemBarStyle.auto(
+                Color.Transparent.toArgb(),
+                Color.Transparent.toArgb()
+            ),
+            navigationBarStyle = SystemBarStyle.auto(
+                Color.Transparent.toArgb(),
+                Color.Transparent.toArgb()
+            )
+        )
         decorView.setOnApplyWindowInsetsListener(this)
         if (background == null) {
             try {

+ 0 - 2
tvlib/src/main/kotlin/org/mjdev/tvlib/application/TvApplication.kt

@@ -15,11 +15,9 @@ import com.google.android.material.color.DynamicColors
 import org.mjdev.tvlib.events.EventBusInitializer
 
 open class TvApplication : Application() {
-
     override fun onCreate() {
         super.onCreate()
         DynamicColors.applyToActivitiesIfAvailable(this)
         EventBusInitializer.init(this)
     }
-
 }

+ 93 - 91
tvlib/src/main/kotlin/org/mjdev/tvlib/auth/AuthManager.kt

@@ -13,18 +13,19 @@ import android.content.Context
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
 import androidx.compose.ui.platform.LocalContext
-import com.auth0.android.Auth0
-import com.auth0.android.authentication.AuthenticationException
-import com.auth0.android.callback.Callback
-import com.auth0.android.jwt.JWT
-import com.auth0.android.provider.BrowserPicker
-import com.auth0.android.provider.CustomTabsOptions
-import com.auth0.android.provider.WebAuthProvider
-import com.auth0.android.result.Credentials
+//import com.auth0.android.Auth0
+//import com.auth0.android.authentication.AuthenticationException
+//import com.auth0.android.callback.Callback
+//import com.auth0.android.jwt.JWT
+//import com.auth0.android.provider.BrowserPicker
+//import com.auth0.android.provider.CustomTabsOptions
+//import com.auth0.android.provider.WebAuthProvider
+//import com.auth0.android.result.Credentials
 import org.mjdev.tvlib.R
 import org.mjdev.tvlib.data.local.User
 import timber.log.Timber
 
+// todo new auth
 @Suppress("unused")
 class AuthManager(
     val context: Context,
@@ -37,24 +38,25 @@ class AuthManager(
 
     var idToken: String? = null
     val isUserLoggedIn: Boolean get() = ((idToken != null) && (user != null))
-    private val account: Auth0 by lazy { Auth0(clientId, domain) }
+//    private val account: Auth0 by lazy { Auth0(clientId, domain) }
 
     val user: User?
         get() = try {
-            JWT(idToken.toString()).let { jwt ->
-                User().apply {
-                    sub = jwt.subject ?: ""
-                    fullName = jwt.getClaim("name").asString() ?: ""
-                    email = jwt.getClaim("email").asString() ?: ""
-                    emailVerified = jwt.getClaim("email_verified").asBoolean() ?: false
-                    pictureUrl = jwt.getClaim("picture").asString() ?: ""
-                    lastUpdated = jwt.getClaim("updated_at").asString() ?: ""
-                    givenName = jwt.getClaim("given_name").asString() ?: ""
-                    familyName = jwt.getClaim("family_name").asString() ?: ""
-                    nickname = jwt.getClaim("nickname").asString() ?: ""
-                    locale = jwt.getClaim("locale").asString() ?: "en"
-                }
-            }
+            null
+//            JWT(idToken.toString()).let { jwt ->
+//                User().apply {
+//                    sub = jwt.subject ?: ""
+//                    fullName = jwt.getClaim("name").asString() ?: ""
+//                    email = jwt.getClaim("email").asString() ?: ""
+//                    emailVerified = jwt.getClaim("email_verified").asBoolean() ?: false
+//                    pictureUrl = jwt.getClaim("picture").asString() ?: ""
+//                    lastUpdated = jwt.getClaim("updated_at").asString() ?: ""
+//                    givenName = jwt.getClaim("given_name").asString() ?: ""
+//                    familyName = jwt.getClaim("family_name").asString() ?: ""
+//                    nickname = jwt.getClaim("nickname").asString() ?: ""
+//                    locale = jwt.getClaim("locale").asString() ?: "en"
+//                }
+//            }
         } catch (e: Exception) {
             // todo bad token
             Timber.e(e)
@@ -62,82 +64,82 @@ class AuthManager(
         }
 
     init {
-        idToken = context
-            .getSharedPreferences(SAVE_KEY, Context.MODE_PRIVATE)
-            .getString(SAVE_KEY, null)
+//        idToken = context
+//            .getSharedPreferences(SAVE_KEY, Context.MODE_PRIVATE)
+//            .getString(SAVE_KEY, null)
         onUserChange?.invoke(this, user)
     }
 
-    private fun saveToken() {
-        context.getSharedPreferences(SAVE_KEY, Context.MODE_PRIVATE).also { sp ->
-            sp.edit().putString(SAVE_KEY, idToken).apply()
-        }
-    }
+//    private fun saveToken() {
+//        context.getSharedPreferences(SAVE_KEY, Context.MODE_PRIVATE).also { sp ->
+//            sp.edit().putString(SAVE_KEY, idToken).apply()
+//        }
+//    }
 
     fun login() {
-        WebAuthProvider
-            .login(account)
-            .withScheme(scheme)
-            .withTrustedWebActivity()
-            .withCustomTabsOptions(
-                CustomTabsOptions.newBuilder()
-                    .showTitle(false)
-                    .withBrowserPicker(
-                        BrowserPicker.newBuilder()
-                            .withAllowedPackages(allowedPackages)
-                            .build()
-                    )
-                    .build()
-            )
-            .start(
-                context,
-                object : Callback<Credentials, AuthenticationException> {
-                    override fun onFailure(error: AuthenticationException) {
-                        idToken = ""
-                        saveToken()
-                        onUserChange?.invoke(this@AuthManager, user)
-                        Timber.e("Error occurred in login(): $error")
-                    }
-
-                    override fun onSuccess(result: Credentials) {
-                        idToken = result.idToken
-                        saveToken()
-                        onUserChange?.invoke(this@AuthManager, user)
-                        Timber.d("User successfully logged in.")
-                    }
-                }
-            )
+//        WebAuthProvider
+//            .login(account)
+//            .withScheme(scheme)
+//            .withTrustedWebActivity()
+//            .withCustomTabsOptions(
+//                CustomTabsOptions.newBuilder()
+//                    .showTitle(false)
+//                    .withBrowserPicker(
+//                        BrowserPicker.newBuilder()
+//                            .withAllowedPackages(allowedPackages)
+//                            .build()
+//                    )
+//                    .build()
+//            )
+//            .start(
+//                context,
+//                object : Callback<Credentials, AuthenticationException> {
+//                    override fun onFailure(error: AuthenticationException) {
+//                        idToken = ""
+//                        saveToken()
+//                        onUserChange?.invoke(this@AuthManager, user)
+//                        Timber.e("Error occurred in login(): $error")
+//                    }
+//
+//                    override fun onSuccess(result: Credentials) {
+//                        idToken = result.idToken
+//                        saveToken()
+//                        onUserChange?.invoke(this@AuthManager, user)
+//                        Timber.d("User successfully logged in.")
+//                    }
+//                }
+//            )
     }
 
     fun logout() {
-        WebAuthProvider
-            .logout(account)
-            .withScheme(scheme)
-            .withTrustedWebActivity()
-            .withCustomTabsOptions(
-                CustomTabsOptions.newBuilder()
-                    .showTitle(false)
-                    .withBrowserPicker(
-                        BrowserPicker.newBuilder()
-                            .withAllowedPackages(allowedPackages)
-                            .build()
-                    )
-                    .build()
-            )
-            .start(
-                context,
-                object : Callback<Void?, AuthenticationException> {
-                    override fun onFailure(error: AuthenticationException) {
-                        Timber.e("Error occurred in logout(): $error")
-                    }
-
-                    override fun onSuccess(result: Void?) {
-                        idToken = ""
-                        saveToken()
-                        onUserChange?.invoke(this@AuthManager, user)
-                    }
-                }
-            )
+//        WebAuthProvider
+//            .logout(account)
+//            .withScheme(scheme)
+//            .withTrustedWebActivity()
+//            .withCustomTabsOptions(
+//                CustomTabsOptions.newBuilder()
+//                    .showTitle(false)
+//                    .withBrowserPicker(
+//                        BrowserPicker.newBuilder()
+//                            .withAllowedPackages(allowedPackages)
+//                            .build()
+//                    )
+//                    .build()
+//            )
+//            .start(
+//                context,
+//                object : Callback<Void?, AuthenticationException> {
+//                    override fun onFailure(error: AuthenticationException) {
+//                        Timber.e("Error occurred in logout(): $error")
+//                    }
+//
+//                    override fun onSuccess(result: Void?) {
+//                        idToken = ""
+//                        saveToken()
+//                        onUserChange?.invoke(this@AuthManager, user)
+//                    }
+//                }
+//            )
     }
 
     companion object {

+ 0 - 159
tvlib/src/main/kotlin/org/mjdev/tvlib/extensions/HiltExt.kt

@@ -1,159 +0,0 @@
-/*
- *  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 android.app.Activity
-import android.content.Context
-import android.os.Bundle
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.platform.LocalContext
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
-import androidx.lifecycle.ViewModelStoreOwner
-import androidx.lifecycle.viewmodel.CreationExtras
-import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
-import androidx.lifecycle.viewmodel.compose.viewModel
-import androidx.navigation.NavBackStackEntry
-import androidx.savedstate.SavedStateRegistryOwner
-import dagger.hilt.EntryPoint
-import dagger.hilt.EntryPoints
-import dagger.hilt.InstallIn
-import dagger.hilt.android.components.ActivityComponent
-import dagger.hilt.android.internal.builders.ViewModelComponentBuilder
-import dagger.hilt.android.internal.lifecycle.HiltViewModelFactory
-import dagger.hilt.android.internal.lifecycle.HiltViewModelMap
-import org.mjdev.tvlib.extensions.ComposeExt.isEditMode
-import org.mjdev.tvlib.extensions.ContextExt.activity
-import kotlin.reflect.KClass
-import kotlin.reflect.full.companionObject
-import kotlin.reflect.full.functions
-
-@Suppress("unused")
-object HiltExt {
-
-    @Suppress("LocalVariableName")
-    @Composable
-    inline fun <reified VM : ViewModel> appViewModel(
-        viewModelStoreOwner: ViewModelStoreOwner? = null,
-        key: String? = null,
-        mockFnName: String = String.format("mock%s", VM::class.simpleName),
-        companion: KClass<*>? = VM::class.companionObject,
-        noinline modelMockFnc: ((context: Context) -> VM)? = { context ->
-            companion?.functions?.firstOrNull { fn ->
-                fn.name == mockFnName
-            }?.call(companion, context) as VM
-        }
-    ): VM {
-        val _viewModelStoreOwner = (viewModelStoreOwner ?: LocalViewModelStoreOwner.current)
-            ?: throw (RuntimeException("No view model store."))
-        val modelCreator: MutableMap<KClass<*>, (context: Context) -> Any>.() -> Unit = {
-            put(VM::class) { context ->
-                modelMockFnc?.invoke(context) as VM
-            }
-        }
-        return viewModel(
-            viewModelStoreOwner = _viewModelStoreOwner,
-            key = key,
-            factory = createHiltViewModelFactory(
-                _viewModelStoreOwner,
-                modelCreator
-            ),
-        )
-    }
-
-    @Composable
-    fun createHiltViewModelFactory(
-        viewModelStoreOwner: ViewModelStoreOwner,
-        modelCreator: (MutableMap<KClass<*>, (context: Context) -> Any>.() -> Unit)? = null
-    ): ViewModelProvider.Factory = hiltViewModelFactory(
-        context = LocalContext.current,
-        viewModelStoreOwner = viewModelStoreOwner,
-        modelCreator = modelCreator
-    )
-
-    @Composable
-    fun hiltViewModelFactory(
-        context: Context,
-        viewModelStoreOwner: ViewModelStoreOwner,
-        modelCreator: (MutableMap<KClass<*>, (context: Context) -> Any>.() -> Unit)? = null
-    ): ViewModelProvider.Factory {
-        val navBackStackEntry: NavBackStackEntry? = viewModelStoreOwner as? NavBackStackEntry
-        return if (isEditMode() || navBackStackEntry == null)
-            createInternal(context, modelCreator)
-        else {
-            val activity: Activity = context.activity {
-                "Expected an activity context for creating a HiltViewModelFactory for a " +
-                        "NavBackStackEntry but instead found: $context"
-            }
-            createInternal(
-                activity,
-                navBackStackEntry,
-                navBackStackEntry.arguments,
-                navBackStackEntry.defaultViewModelProviderFactory,
-            )
-        }
-    }
-
-    @Suppress("UNUSED_PARAMETER")
-    private fun createInternal(
-        activity: Activity,
-        owner: SavedStateRegistryOwner,
-        defaultArgs: Bundle?,
-        delegateFactory: ViewModelProvider.Factory
-    ): ViewModelProvider.Factory {
-        return createInternal(activity, delegateFactory)
-    }
-
-    private fun createInternal(
-        activity: Activity, delegateFactory: ViewModelProvider.Factory
-    ): ViewModelProvider.Factory {
-        val entryPoint = EntryPoints.get(
-            activity,
-            ActivityCreatorEntryPoint::class.java
-        )
-        return HiltViewModelFactory(
-            entryPoint.viewModelKeys,
-            delegateFactory,
-            entryPoint.viewModelComponentBuilder
-        )
-    }
-
-    @EntryPoint
-    @InstallIn(ActivityComponent::class)
-    internal interface ActivityCreatorEntryPoint {
-        @get:HiltViewModelMap.KeySet
-        val viewModelKeys: Map<Class<*>, Boolean>
-        val viewModelComponentBuilder: ViewModelComponentBuilder
-    }
-
-    private fun createInternal(
-        context: Context,
-        modelCreator: (MutableMap<KClass<*>, (context: Context) -> Any>.() -> Unit)?
-    ): ViewModelProvider.Factory = MockViewModelFactory(context, modelCreator)
-
-    @Suppress("UNCHECKED_CAST")
-    class MockViewModelFactory(
-        val context: Context,
-        modelCreator: (MutableMap<KClass<*>, (context: Context) -> Any>.() -> Unit)?
-    ) : ViewModelProvider.Factory {
-
-        private val store = mutableMapOf<KClass<*>, (context: Context) -> Any>()
-            .apply { modelCreator?.invoke(this) }
-
-        override fun <T : ViewModel> create(modelClass: Class<T>): T {
-            return store[modelClass.kotlin]?.invoke(context) as T
-        }
-
-        override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
-            return store[modelClass.kotlin]?.invoke(context) as T
-        }
-
-    }
-
-}

+ 7 - 4
tvlib/src/main/kotlin/org/mjdev/tvlib/viewmodel/BaseViewModel.kt

@@ -8,8 +8,10 @@
 
 package org.mjdev.tvlib.viewmodel
 
+import android.app.Application
+import android.content.Context
+import androidx.lifecycle.AndroidViewModel
 import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.flow.Flow
@@ -21,9 +23,10 @@ import kotlinx.coroutines.flow.stateIn
 import kotlinx.coroutines.launch
 import timber.log.Timber
 
-@Suppress("unused")
-open class BaseViewModel() : ViewModel() {
-
+@Suppress("unused", "StaticFieldLeak")
+open class BaseViewModel(
+    context: Context
+) : AndroidViewModel(context.applicationContext as Application) {
     val error: MutableStateFlow<Throwable?> = MutableStateFlow(null)
 
     private var errorHandler: (error: Throwable) -> Unit = { e -> Timber.e(e) }

+ 0 - 0
web/install.kts


+ 0 - 44
web/install.sh

@@ -1,44 +0,0 @@
-#!/bin/sh
-#
-#  Copyright (c) Milan Jurkulák 2024.
-#  Contact:
-#  e: mimoccc@gmail.com
-#  e: mj@mjdev.org
-#  w: https://mjdev.org
-#
-service_name="app"
-# store actual dir
-pwd=$(pwd)
-# sw needed
-sudo apt-get install.sh certbot nginx-full nodejs npm ufw yarn
-# stop service
-sudo systemctl stop "service-$service_name"
-sudo systemctl disable "service-$service_name"
-# remove old
-sudo rm -rf "/etc/systemd/system/service-$service_name.service"
-sudo rm -rf "/etc/nginx/sites-available/service-$service_name.nginx"
-sudo rm -rf "/etc/nginx/sites-enabled/service-$service_name.nginx"
-# reload services
-sudo systemctl daemon-reload
-sudo systemctl reload nginx
-# copy things
-sudo mkdir -p "/opt/services/$service_name"
-sudo cp -rf * "/opt/services/$service_name"
-# link things
-sudo ln -s "/opt/services/$service_name/$service_name.service" "/etc/systemd/system/service-$service_name.service"
-sudo ln -s "/opt/services/$service_name/$service_name.nginx" "/etc/nginx/sites-available/service-$service_name.nginx"
-sudo ln -s "/opt/services/$service_name/$service_name.nginx" "/etc/nginx/sites-enabled/service-$service_name.nginx"
-# go to directory
-cd "/opt/services/$service_name" || exit
-# build
-sudo npm install.sh
-sudo npm audit fix
-sudo npm upgrade
-#sudo npm run build
-# go back
-cd "$pwd" || exit
-# start services
-sudo systemctl daemon-reload
-sudo systemctl enable "service-$service_name"
-sudo systemctl start "service-$service_name"
-sudo systemctl reload nginx

+ 0 - 0
web/uninstall.kts


+ 0 - 19
web/uninstall.sh

@@ -1,19 +0,0 @@
-#!/bin/sh
-#
-#  Copyright (c) Milan Jurkulák 2024.
-#  Contact:
-#  e: mimoccc@gmail.com
-#  e: mj@mjdev.org
-#  w: https://mjdev.org
-#
-service_name="app"
-# stop service
-sudo systemctl stop "service-$service_name"
-sudo systemctl disable "service-$service_name"
-# remove old
-sudo rm -rf "/etc/systemd/system/service-$service_name.service"
-sudo rm -rf "/etc/nginx/sites-available/service-$service_name.nginx"
-sudo rm -rf "/etc/nginx/sites-enabled/service-$service_name.nginx"
-# reload services
-sudo systemctl daemon-reload
-sudo systemctl reload nginx