Przeglądaj źródła

code improvements

code improvements

code improvements

code improvements

code improvements
Milan Jurkulak 2 lat temu
rodzic
commit
607f613e8c
46 zmienionych plików z 465 dodań i 383 usunięć
  1. 11 5
      app/build.gradle.kts
  2. 18 81
      app/objectbox-models/default.json
  3. 35 1
      app/objectbox-models/default.json.bak
  4. 9 2
      app/proguard-rules.pro
  5. 0 10
      app/src/main/AndroidManifest.xml
  6. 4 11
      app/src/main/kotlin/org/mjdev/tvapp/activity/IPTVActivity.kt
  7. 4 4
      app/src/main/kotlin/org/mjdev/tvapp/activity/MainActivity.kt
  8. 2 7
      app/src/main/kotlin/org/mjdev/tvapp/activity/WebActivity.kt
  9. 13 48
      app/src/main/kotlin/org/mjdev/tvapp/app/Application.kt
  10. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/data/local/Category.kt
  11. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/data/local/Country.kt
  12. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/data/local/Message.kt
  13. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/data/local/Movie.kt
  14. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/data/local/TVChannel.kt
  15. 2 1
      app/src/main/kotlin/org/mjdev/tvapp/data/remote/Category.kt
  16. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/data/remote/Channel.kt
  17. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/data/remote/Country.kt
  18. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/data/remote/Epg.kt
  19. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/data/remote/Language.kt
  20. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/data/remote/Stream.kt
  21. 0 32
      app/src/main/kotlin/org/mjdev/tvapp/database/convert/CustomPropertyConverter.kt
  22. 0 22
      app/src/main/kotlin/org/mjdev/tvapp/database/convert/EncryptionConverter.kt
  23. 0 23
      app/src/main/kotlin/org/mjdev/tvapp/database/convert/PropertyEnumConverter.kt
  24. 0 21
      app/src/main/kotlin/org/mjdev/tvapp/database/convert/PropertyJsonConverter.kt
  25. 0 33
      app/src/main/kotlin/org/mjdev/tvapp/database/convert/StringListConverter.kt
  26. 0 34
      app/src/main/kotlin/org/mjdev/tvapp/helpers/SyncAdapterInitializer.kt
  27. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/sync/Authenticator.kt
  28. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/sync/AuthenticatorService.kt
  29. 2 0
      app/src/main/kotlin/org/mjdev/tvapp/sync/DataProvider.kt
  30. 4 0
      app/src/main/kotlin/org/mjdev/tvapp/sync/SyncAdapter.kt
  31. 3 0
      app/src/main/kotlin/org/mjdev/tvapp/sync/SyncService.kt
  32. 5 3
      app/src/main/kotlin/org/mjdev/tvapp/ui/pages/MainPage.kt
  33. 4 1
      tvlib/build.gradle.kts
  34. 24 17
      tvlib/src/main/kotlin/org/mjdev/tvlib/auth/AuthManager.kt
  35. 3 5
      tvlib/src/main/kotlin/org/mjdev/tvlib/data/local/User.kt
  36. 32 0
      tvlib/src/main/kotlin/org/mjdev/tvlib/database/convert/CustomPropertyConverter.kt
  37. 30 0
      tvlib/src/main/kotlin/org/mjdev/tvlib/database/convert/EncryptionConverter.kt
  38. 31 0
      tvlib/src/main/kotlin/org/mjdev/tvlib/database/convert/PropertyEnumConverter.kt
  39. 29 0
      tvlib/src/main/kotlin/org/mjdev/tvlib/database/convert/PropertyJsonConverter.kt
  40. 41 0
      tvlib/src/main/kotlin/org/mjdev/tvlib/database/convert/StringListConverter.kt
  41. 44 14
      tvlib/src/main/kotlin/org/mjdev/tvlib/extensions/NavGraphBuilderExt.kt
  42. 3 1
      tvlib/src/main/kotlin/org/mjdev/tvlib/glide/ApplicationGlideApp.kt
  43. 78 0
      tvlib/src/main/kotlin/org/mjdev/tvlib/glide/sources/m3u/M3ULoaderFactory.kt
  44. 2 3
      tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/tv/NeonRectangle.kt
  45. 5 4
      tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/web/WebView.kt
  46. 3 0
      tvlib/src/main/res/values/strings.xml

+ 11 - 5
app/build.gradle.kts

@@ -80,6 +80,9 @@ android {
             applicationIdSuffix = ".debug"
             applicationIdSuffix = ".debug"
             isDebuggable = true
             isDebuggable = true
             isMinifyEnabled = false
             isMinifyEnabled = false
+            isShrinkResources = false
+//            isCrunchPngs = true
+//            isEmbedMicroApp = true
             proguardFiles(
             proguardFiles(
                 getDefaultProguardFile("proguard-android-optimize.txt"),
                 getDefaultProguardFile("proguard-android-optimize.txt"),
                 "proguard-rules.pro"
                 "proguard-rules.pro"
@@ -99,7 +102,10 @@ android {
         release {
         release {
             applicationIdSuffix = ""
             applicationIdSuffix = ""
             isDebuggable = false
             isDebuggable = false
-            isMinifyEnabled = false // todo
+            isMinifyEnabled = true
+            isShrinkResources = true
+//            isCrunchPngs = true
+//            isEmbedMicroApp = true
             proguardFiles(
             proguardFiles(
                 getDefaultProguardFile("proguard-android-optimize.txt"),
                 getDefaultProguardFile("proguard-android-optimize.txt"),
                 "proguard-rules.pro"
                 "proguard-rules.pro"
@@ -120,6 +126,9 @@ android {
             applicationIdSuffix = ".rcmps"
             applicationIdSuffix = ".rcmps"
             isDebuggable = true
             isDebuggable = true
             isMinifyEnabled = false
             isMinifyEnabled = false
+            isShrinkResources = false
+//            isCrunchPngs = true
+//            isEmbedMicroApp = true
             proguardFiles(
             proguardFiles(
                 getDefaultProguardFile("proguard-android-optimize.txt"),
                 getDefaultProguardFile("proguard-android-optimize.txt"),
                 "proguard-rules.pro"
                 "proguard-rules.pro"
@@ -186,7 +195,7 @@ dependencies {
     // todo remove
     // todo remove
     implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.21")
     implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.21")
     // startups
     // startups
-    implementation("androidx.startup:startup-runtime:1.1.1")
+//    implementation("androidx.startup:startup-runtime:1.1.1")
     // tv library by mjde milan jurkulak
     // tv library by mjde milan jurkulak
     implementation(project(mapOf("path" to ":tvlib")))
     implementation(project(mapOf("path" to ":tvlib")))
     // base libs
     // base libs
@@ -241,9 +250,6 @@ dependencies {
     debugImplementation("androidx.customview:customview-poolingcontainer:1.0.0")
     debugImplementation("androidx.customview:customview-poolingcontainer:1.0.0")
     // lottie
     // lottie
     implementation("com.airbnb.android:lottie-compose:6.2.0")
     implementation("com.airbnb.android:lottie-compose:6.2.0")
-    // oauth
-    implementation("com.auth0.android:auth0:2.10.2")
-    implementation("com.auth0.android:jwtdecode:2.0.2")
     // widgets
     // widgets
     implementation("androidx.glance:glance-appwidget:1.0.0")
     implementation("androidx.glance:glance-appwidget:1.0.0")
     implementation("androidx.glance:glance-material:1.0.0")
     implementation("androidx.glance:glance-material:1.0.0")

+ 18 - 81
app/objectbox-models/default.json

@@ -215,85 +215,6 @@
       ],
       ],
       "relations": []
       "relations": []
     },
     },
-    {
-      "id": "4:1987880888030669889",
-      "lastPropertyId": "14:6587709986428549544",
-      "name": "User",
-      "properties": [
-        {
-          "id": "1:8489281024233193571",
-          "name": "id",
-          "type": 6,
-          "flags": 1
-        },
-        {
-          "id": "2:3068422320211502547",
-          "name": "sub",
-          "type": 9
-        },
-        {
-          "id": "3:7880080743593438568",
-          "name": "givenName",
-          "type": 9
-        },
-        {
-          "id": "4:7091128848714558596",
-          "name": "familyName",
-          "type": 9
-        },
-        {
-          "id": "5:3275722102836586086",
-          "name": "nickname",
-          "type": 9
-        },
-        {
-          "id": "6:539261702815051564",
-          "name": "fullName",
-          "type": 9
-        },
-        {
-          "id": "7:5697902777673765209",
-          "name": "pictureUrl",
-          "type": 9
-        },
-        {
-          "id": "8:1398381634980613029",
-          "name": "locale",
-          "type": 9
-        },
-        {
-          "id": "9:7614335227423573086",
-          "name": "lastUpdated",
-          "type": 9
-        },
-        {
-          "id": "10:3865959571559170686",
-          "name": "pinCode",
-          "type": 9
-        },
-        {
-          "id": "11:5012672523655215079",
-          "name": "settingsJson",
-          "type": 9
-        },
-        {
-          "id": "12:7864280554037368021",
-          "name": "age",
-          "type": 5
-        },
-        {
-          "id": "13:3813998053361806387",
-          "name": "email",
-          "type": 9
-        },
-        {
-          "id": "14:6587709986428549544",
-          "name": "emailVerified",
-          "type": 1
-        }
-      ],
-      "relations": []
-    },
     {
     {
       "id": "5:6626640907745307806",
       "id": "5:6626640907745307806",
       "lastPropertyId": "4:4895179631110667602",
       "lastPropertyId": "4:4895179631110667602",
@@ -364,11 +285,27 @@
   "lastSequenceId": "0:0",
   "lastSequenceId": "0:0",
   "modelVersion": 5,
   "modelVersion": 5,
   "modelVersionParserMinimum": 5,
   "modelVersionParserMinimum": 5,
-  "retiredEntityUids": [],
+  "retiredEntityUids": [
+    1987880888030669889
+  ],
   "retiredIndexUids": [],
   "retiredIndexUids": [],
   "retiredPropertyUids": [
   "retiredPropertyUids": [
     2336450843545835846,
     2336450843545835846,
-    1211648868008137353
+    1211648868008137353,
+    8489281024233193571,
+    3068422320211502547,
+    7880080743593438568,
+    7091128848714558596,
+    3275722102836586086,
+    539261702815051564,
+    5697902777673765209,
+    1398381634980613029,
+    7614335227423573086,
+    3865959571559170686,
+    5012672523655215079,
+    7864280554037368021,
+    3813998053361806387,
+    6587709986428549544
   ],
   ],
   "retiredRelationUids": [],
   "retiredRelationUids": [],
   "version": 1
   "version": 1

+ 35 - 1
app/objectbox-models/default.json.bak

@@ -322,9 +322,43 @@
         }
         }
       ],
       ],
       "relations": []
       "relations": []
+    },
+    {
+      "id": "6:8612359832519392835",
+      "lastPropertyId": "5:3948204030396009919",
+      "name": "Country",
+      "properties": [
+        {
+          "id": "1:5421436411115442406",
+          "name": "id",
+          "type": 6,
+          "flags": 1
+        },
+        {
+          "id": "2:5924521886771099371",
+          "name": "code",
+          "type": 9
+        },
+        {
+          "id": "3:3406479931615019718",
+          "name": "name",
+          "type": 9
+        },
+        {
+          "id": "4:2189339425724407936",
+          "name": "languages",
+          "type": 30
+        },
+        {
+          "id": "5:3948204030396009919",
+          "name": "flag",
+          "type": 9
+        }
+      ],
+      "relations": []
     }
     }
   ],
   ],
-  "lastEntityId": "5:6626640907745307806",
+  "lastEntityId": "6:8612359832519392835",
   "lastIndexId": "0:0",
   "lastIndexId": "0:0",
   "lastRelationId": "0:0",
   "lastRelationId": "0:0",
   "lastSequenceId": "0:0",
   "lastSequenceId": "0:0",

+ 9 - 2
app/proguard-rules.pro

@@ -14,8 +14,15 @@
 
 
 # Uncomment this to preserve the line number information for
 # Uncomment this to preserve the line number information for
 # debugging stack traces.
 # debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
+-keepattributes SourceFile,LineNumberTable
 
 
 # If you keep the line number information, uncomment this to
 # If you keep the line number information, uncomment this to
 # hide the original source file name.
 # hide the original source file name.
-#-renamesourcefileattribute SourceFile
+#-renamesourcefileattribute SourceFile
+
+-dontwarn javax.servlet.ServletContainerInitializer
+-dontwarn org.apache.bsf.BSFManager
+
+-keep,allowobfuscation,allowshrinking interface retrofit2.Call
+-keep,allowobfuscation,allowshrinking class retrofit2.Response
+-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation

+ 0 - 10
app/src/main/AndroidManifest.xml

@@ -219,16 +219,6 @@
             android:syncable="true"
             android:syncable="true"
             tools:ignore="ExportedContentProvider" />
             tools:ignore="ExportedContentProvider" />
 
 
-        <provider
-            android:name="androidx.startup.InitializationProvider"
-            android:authorities="${applicationId}.androidx-startup"
-            android:exported="false"
-            tools:node="merge">
-            <meta-data
-                android:name="org.mjdev.tvapp.helpers.SyncAdapterInitializer"
-                android:value="androidx.startup" />
-        </provider>
-
         <receiver
         <receiver
             android:name=".widget.MyAppWidgetReceiver"
             android:name=".widget.MyAppWidgetReceiver"
             android:exported="true">
             android:exported="true">

+ 4 - 11
app/src/main/kotlin/org/mjdev/tvapp/activity/IPTVActivity.kt

@@ -20,6 +20,7 @@ import org.mjdev.tvapp.ui.screens.LoadingScreen
 import org.mjdev.tvlib.activity.TvActivity
 import org.mjdev.tvlib.activity.TvActivity
 import org.mjdev.tvlib.annotations.Previews
 import org.mjdev.tvlib.annotations.Previews
 import org.mjdev.tvlib.extensions.NavControllerExt.openAsTop
 import org.mjdev.tvlib.extensions.NavControllerExt.openAsTop
+import org.mjdev.tvlib.extensions.NavGraphBuilderExt.startScreen
 import org.mjdev.tvlib.interfaces.ItemAudio
 import org.mjdev.tvlib.interfaces.ItemAudio
 import org.mjdev.tvlib.interfaces.ItemPhoto
 import org.mjdev.tvlib.interfaces.ItemPhoto
 import org.mjdev.tvlib.interfaces.ItemVideo
 import org.mjdev.tvlib.interfaces.ItemVideo
@@ -33,15 +34,9 @@ class IPTVActivity : TvActivity() {
     override fun Compose() = super.Compose()
     override fun Compose() = super.Compose()
 
 
     override val navGraphBuilder: NavGraphBuilderEx.() -> Unit = {
     override val navGraphBuilder: NavGraphBuilderEx.() -> Unit = {
-
-        screen(
-            route = LoadingScreen(),
-            isHomeScreen = true,
-            isStartScreen = true,
-        )
-        screen(route = IPTVScreen())
-        screen(route = GalleryScreen())
-
+        startScreen(LoadingScreen(), true)
+        screen(IPTVScreen())
+        screen(GalleryScreen())
     }
     }
 
 
     @Suppress("DEPRECATION", "MoveVariableDeclarationIntoWhen")
     @Suppress("DEPRECATION", "MoveVariableDeclarationIntoWhen")
@@ -58,9 +53,7 @@ class IPTVActivity : TvActivity() {
     }
     }
 
 
     companion object {
     companion object {
-
         const val IPTV_DATA = "IPTV_DATA"
         const val IPTV_DATA = "IPTV_DATA"
-
     }
     }
 
 
 }
 }

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

@@ -16,6 +16,8 @@ import org.mjdev.tvapp.ui.screens.MainScreen
 import org.mjdev.tvapp.ui.screens.SplashScreen
 import org.mjdev.tvapp.ui.screens.SplashScreen
 import org.mjdev.tvlib.activity.TvActivity
 import org.mjdev.tvlib.activity.TvActivity
 import org.mjdev.tvlib.annotations.Previews
 import org.mjdev.tvlib.annotations.Previews
+import org.mjdev.tvlib.extensions.NavGraphBuilderExt.homeScreen
+import org.mjdev.tvlib.extensions.NavGraphBuilderExt.startScreen
 
 
 @AndroidEntryPoint
 @AndroidEntryPoint
 class MainActivity : TvActivity() {
 class MainActivity : TvActivity() {
@@ -25,10 +27,8 @@ class MainActivity : TvActivity() {
     override fun Compose() = super.Compose()
     override fun Compose() = super.Compose()
 
 
     override val navGraphBuilder: NavGraphBuilderEx.() -> Unit = {
     override val navGraphBuilder: NavGraphBuilderEx.() -> Unit = {
-
-        screen(route = SplashScreen(), isStartScreen = true)
-        screen(route = MainScreen(), isHomeScreen = true)
-
+        startScreen(SplashScreen())
+        homeScreen(MainScreen())
     }
     }
 
 
 }
 }

+ 2 - 7
app/src/main/kotlin/org/mjdev/tvapp/activity/WebActivity.kt

@@ -18,6 +18,7 @@ import org.mjdev.tvlib.navigation.NavGraphBuilderEx
 import org.mjdev.tvapp.ui.screens.WebScreen
 import org.mjdev.tvapp.ui.screens.WebScreen
 import org.mjdev.tvlib.annotations.Previews
 import org.mjdev.tvlib.annotations.Previews
 import org.mjdev.tvlib.extensions.NavControllerExt.openAsTop
 import org.mjdev.tvlib.extensions.NavControllerExt.openAsTop
+import org.mjdev.tvlib.extensions.NavGraphBuilderExt.startScreen
 import org.mjdev.tvlib.extensions.StringExt.parseUri
 import org.mjdev.tvlib.extensions.StringExt.parseUri
 import org.mjdev.tvlib.navigation.NavHostControllerEx
 import org.mjdev.tvlib.navigation.NavHostControllerEx
 
 
@@ -29,13 +30,7 @@ class WebActivity : ComposableActivity() {
     override fun Compose() = super.Compose()
     override fun Compose() = super.Compose()
 
 
     override val navGraphBuilder: NavGraphBuilderEx.() -> Unit = {
     override val navGraphBuilder: NavGraphBuilderEx.() -> Unit = {
-
-        screen(
-            route = WebScreen(),
-            isHomeScreen = true,
-            isStartScreen = true,
-        )
-
+        startScreen(WebScreen(), true)
     }
     }
 
 
     override fun onIntent(navController: NavHostControllerEx, intent: Intent?) {
     override fun onIntent(navController: NavHostControllerEx, intent: Intent?) {

+ 13 - 48
app/src/main/kotlin/org/mjdev/tvapp/app/Application.kt

@@ -9,59 +9,24 @@
 package org.mjdev.tvapp.app
 package org.mjdev.tvapp.app
 
 
 import dagger.hilt.android.HiltAndroidApp
 import dagger.hilt.android.HiltAndroidApp
+import kotlinx.coroutines.Dispatchers
+import org.mjdev.tvapp.sync.SyncService.Companion.createSyncAccount
+import org.mjdev.tvapp.sync.SyncService.Companion.requestSync
 import org.mjdev.tvlib.application.TvApplication
 import org.mjdev.tvlib.application.TvApplication
+import org.mjdev.tvlib.extensions.GlobalExt.launch
 
 
 @HiltAndroidApp
 @HiltAndroidApp
 class Application : TvApplication() {
 class Application : TvApplication() {
 
 
-//    private val imageLoader by lazy {
-//        ImageLoader.Builder(this)
-//            .allowHardware(false)
-////            .allowRgb565(true)
-//            .addLastModifiedToFileCacheKey(false)
-//            .crossfade(false)
-//            .diskCachePolicy(CachePolicy.ENABLED)
-//            .memoryCachePolicy(CachePolicy.ENABLED)
-//            .respectCacheHeaders(false)
-//            .networkObserverEnabled(false)
-//            .okHttpClient {
-//                OkHttpClient.Builder()
-//                    .cache(
-//                        Cache(
-//                            directory = File(
-//                                cacheDir,
-//                                "http_cache"
-//                            ),
-//                            maxSize = 1024L * 1024L * 1024L
-//                        )
-//                    )
-//                    .addNetworkInterceptor(HttpLoggingInterceptor().apply {
-//                        level = HttpLoggingInterceptor.Level.BODY
-//                    })
-//                    .addNetworkInterceptor(CacheInterceptor())
-//                    .build()
-//            }
-//            .diskCache {
-//                DiskCache.Builder()
-//                    .directory(cacheDir.resolve("image_cache"))
-//                    .maxSizePercent(0.7)
-//                    .build()
-//            }
-//            .components {
-//                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
-//                    add(ImageDecoderDecoder.Factory())
-//                } else {
-//                    add(GifDecoder.Factory())
-//                }
-//                add(AlbumArtDecoder.Factory())
-//                add(VideoFrameDecoder.Factory())
-//                add(SvgDecoder.Factory())
-//            }.build()
-//    }
+    private fun createAccountAndSync() {
+        createSyncAccount()?.let { account ->
+            requestSync(account)
+        }
+    }
 
 
-//    override fun onCreate() {
-//        super.onCreate()
-//    }
+    override fun onCreate() {
+        super.onCreate()
+        createAccountAndSync()
+    }
 
 
-//    override fun createImageLoader(): ImageLoader = imageLoader
 }
 }

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/data/local/Category.kt

@@ -8,6 +8,7 @@
 
 
 package org.mjdev.tvapp.data.local
 package org.mjdev.tvapp.data.local
 
 
+import androidx.annotation.Keep
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
 import io.objectbox.annotation.Entity
 import io.objectbox.annotation.Entity
@@ -18,6 +19,7 @@ import org.mjdev.tvlib.interfaces.ItemWithSubtitle
 import org.mjdev.tvlib.interfaces.ItemWithTitle
 import org.mjdev.tvlib.interfaces.ItemWithTitle
 import java.io.Serializable
 import java.io.Serializable
 
 
+@Keep
 @Suppress("unused")
 @Suppress("unused")
 @Entity
 @Entity
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/data/local/Country.kt

@@ -8,11 +8,13 @@
 
 
 package org.mjdev.tvapp.data.local
 package org.mjdev.tvapp.data.local
 
 
+import androidx.annotation.Keep
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
 import io.objectbox.annotation.Entity
 import io.objectbox.annotation.Entity
 import io.objectbox.annotation.Id
 import io.objectbox.annotation.Id
 
 
+@Keep
 @Suppress("unused")
 @Suppress("unused")
 @Entity
 @Entity
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/data/local/Message.kt

@@ -8,6 +8,7 @@
 
 
 package org.mjdev.tvapp.data.local
 package org.mjdev.tvapp.data.local
 
 
+import androidx.annotation.Keep
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
 import io.objectbox.annotation.Entity
 import io.objectbox.annotation.Entity
@@ -16,6 +17,7 @@ import org.mjdev.tvlib.interfaces.ItemWithId
 import org.mjdev.tvlib.interfaces.ItemWithMessage
 import org.mjdev.tvlib.interfaces.ItemWithMessage
 import org.mjdev.tvlib.interfaces.ItemWithTitle
 import org.mjdev.tvlib.interfaces.ItemWithTitle
 
 
+@Keep
 @Entity
 @Entity
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)
 class Message : ItemWithTitle<String>, ItemWithMessage<String>, ItemWithId {
 class Message : ItemWithTitle<String>, ItemWithMessage<String>, ItemWithId {

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/data/local/Movie.kt

@@ -8,6 +8,7 @@
 
 
 package org.mjdev.tvapp.data.local
 package org.mjdev.tvapp.data.local
 
 
+import androidx.annotation.Keep
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
 import io.objectbox.annotation.Entity
 import io.objectbox.annotation.Entity
@@ -22,6 +23,7 @@ import org.mjdev.tvlib.interfaces.ItemWithUri
 import java.io.Serializable
 import java.io.Serializable
 import java.util.Locale
 import java.util.Locale
 
 
+@Keep
 @Suppress("unused")
 @Suppress("unused")
 @Entity
 @Entity
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/data/local/TVChannel.kt

@@ -8,6 +8,7 @@
 
 
 package org.mjdev.tvapp.data.local
 package org.mjdev.tvapp.data.local
 
 
+import androidx.annotation.Keep
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
 import io.objectbox.annotation.Entity
 import io.objectbox.annotation.Entity
@@ -23,6 +24,7 @@ import org.mjdev.tvlib.interfaces.ItemWithTitle
 import org.mjdev.tvlib.interfaces.ItemWithUri
 import org.mjdev.tvlib.interfaces.ItemWithUri
 import java.io.Serializable
 import java.io.Serializable
 
 
+@Keep
 @Suppress("unused")
 @Suppress("unused")
 @Entity
 @Entity
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)

+ 2 - 1
app/src/main/kotlin/org/mjdev/tvapp/data/remote/Category.kt

@@ -8,10 +8,11 @@
 
 
 package org.mjdev.tvapp.data.remote
 package org.mjdev.tvapp.data.remote
 
 
+import androidx.annotation.Keep
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
-import org.mjdev.tvapp.data.local.User
 
 
+@Keep
 @Suppress("unused")
 @Suppress("unused")
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)
 class Category {
 class Category {

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/data/remote/Channel.kt

@@ -8,9 +8,11 @@
 
 
 package org.mjdev.tvapp.data.remote
 package org.mjdev.tvapp.data.remote
 
 
+import androidx.annotation.Keep
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
 
 
+@Keep
 @Suppress("unused")
 @Suppress("unused")
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)
 class Channel {
 class Channel {

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/data/remote/Country.kt

@@ -8,9 +8,11 @@
 
 
 package org.mjdev.tvapp.data.remote
 package org.mjdev.tvapp.data.remote
 
 
+import androidx.annotation.Keep
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
 
 
+@Keep
 @Suppress("unused")
 @Suppress("unused")
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)
 class Country {
 class Country {

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/data/remote/Epg.kt

@@ -8,9 +8,11 @@
 
 
 package org.mjdev.tvapp.data.remote
 package org.mjdev.tvapp.data.remote
 
 
+import androidx.annotation.Keep
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
 
 
+@Keep
 @Suppress("unused")
 @Suppress("unused")
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)
 class Epg {
 class Epg {

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/data/remote/Language.kt

@@ -8,9 +8,11 @@
 
 
 package org.mjdev.tvapp.data.remote
 package org.mjdev.tvapp.data.remote
 
 
+import androidx.annotation.Keep
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
 
 
+@Keep
 @Suppress("unused")
 @Suppress("unused")
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)
 class Language {
 class Language {

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/data/remote/Stream.kt

@@ -8,9 +8,11 @@
 
 
 package org.mjdev.tvapp.data.remote
 package org.mjdev.tvapp.data.remote
 
 
+import androidx.annotation.Keep
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
 
 
+@Keep
 @Suppress("unused")
 @Suppress("unused")
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)
 class Stream {
 class Stream {

+ 0 - 32
app/src/main/kotlin/org/mjdev/tvapp/database/convert/CustomPropertyConverter.kt

@@ -1,32 +0,0 @@
-/*
- * Copyright (c) Milan Jurkulák 2023.
- *  Contact:
- *  e: mimoccc@gmail.com
- *  e: mj@mjdev.org
- *  w: https://mjdev.org
- */
-
-@file:Suppress("unused")
-
-package org.mjdev.tvapp.database.convert
-
-import io.objectbox.converter.PropertyConverter
-
-open class ConvertEntity : Any() {
-    open val id: Long = 0L
-}
-
-open class CustomPropertyConverter<T : ConvertEntity>(
-
-    private val supplier: (id: Long) -> T?
-
-) : PropertyConverter<T, Long> {
-
-    override fun convertToEntityProperty(databaseValue: Long?): T? =
-        if (databaseValue == null) null
-        else supplier.invoke(databaseValue)
-
-    override fun convertToDatabaseValue(entityProperty: T): Long =
-        entityProperty.id
-
-}

+ 0 - 22
app/src/main/kotlin/org/mjdev/tvapp/database/convert/EncryptionConverter.kt

@@ -1,22 +0,0 @@
-@file:Suppress("unused")
-
-package org.mjdev.tvapp.database.convert
-
-import com.scottyab.aescrypt.AESCrypt
-import io.objectbox.converter.PropertyConverter
-
-class EncryptionConverter : PropertyConverter<String, String> {
-
-    companion object {
-
-        const val AES_KEY = "simply key to encrypt data"
-
-    }
-
-    override fun convertToDatabaseValue(entityProperty: String): String =
-        AESCrypt.encrypt(AES_KEY, entityProperty)
-
-    override fun convertToEntityProperty(databaseValue: String?): String =
-        AESCrypt.decrypt(AES_KEY, databaseValue)
-
-}

+ 0 - 23
app/src/main/kotlin/org/mjdev/tvapp/database/convert/PropertyEnumConverter.kt

@@ -1,23 +0,0 @@
-package org.mjdev.tvapp.database.convert
-
-import io.objectbox.converter.PropertyConverter
-
-@Suppress("unused")
-open class PropertyEnumConverter<T, R>(
-
-    private val values: Array<T>,
-    private val default: T,
-    private val predicate: T.(R) -> Boolean,
-    private val supplier: (T) -> R?
-
-) : PropertyConverter<T, R> {
-
-    override fun convertToEntityProperty(databaseValue: R?): T? =
-        if (databaseValue == null) null
-        else values.firstOrNull { p -> p.predicate(databaseValue) } ?: default
-
-    override fun convertToDatabaseValue(entityProperty: T?): R? =
-        if (entityProperty == null) null
-        else supplier(entityProperty)
-
-}

+ 0 - 21
app/src/main/kotlin/org/mjdev/tvapp/database/convert/PropertyJsonConverter.kt

@@ -1,21 +0,0 @@
-package org.mjdev.tvapp.database.convert
-
-import com.squareup.moshi.Moshi
-import io.objectbox.converter.PropertyConverter
-
-@Suppress("unused")
-open class PropertyJsonConverter<T : Any>(
-
-    private val cls: Class<T>
-
-    ) : PropertyConverter<T, String> {
-
-    override fun convertToEntityProperty(databaseValue: String?): T? =
-        if (databaseValue == null) null
-        else Moshi.Builder().build().adapter(cls).fromJson(databaseValue) as T
-
-    override fun convertToDatabaseValue(entityProperty: T?): String? =
-        if (entityProperty == null) null
-        else Moshi.Builder().build().adapter(cls).toJson(entityProperty)
-
-}

+ 0 - 33
app/src/main/kotlin/org/mjdev/tvapp/database/convert/StringListConverter.kt

@@ -1,33 +0,0 @@
-package org.mjdev.tvapp.database.convert
-
-import io.objectbox.converter.PropertyConverter
-import org.json.JSONArray
-
-@Suppress("unused")
-class StringListConverter : PropertyConverter<List<String?>?, String?> {
-
-    override fun convertToEntityProperty(
-        databaseValue: String?
-    ): List<String?> = if (databaseValue == null) ArrayList()
-    else try {
-        val array = JSONArray(databaseValue)
-        val ret = ArrayList<String?>()
-        for (i in 0 until array.length()) {
-            ret.add(array.getString(i))
-        }
-        ret
-    } catch (e: Exception) {
-        ArrayList()
-    }
-
-    override fun convertToDatabaseValue(
-        entityProperty: List<String?>?
-    ): String? {
-        return try {
-            if (entityProperty == null) null else JSONArray(entityProperty).toString()
-        } catch (e: Exception) {
-            null
-        }
-    }
-
-}

+ 0 - 34
app/src/main/kotlin/org/mjdev/tvapp/helpers/SyncAdapterInitializer.kt

@@ -1,34 +0,0 @@
-/*
- *  Copyright (c) Milan Jurkulák 2023.
- *  Contact:
- *  e: mimoccc@gmail.com
- *  e: mj@mjdev.org
- *  w: https://mjdev.org
- */
-
-package org.mjdev.tvapp.helpers
-
-import android.content.Context
-import androidx.startup.Initializer
-import org.mjdev.tvapp.sync.SyncService
-import org.mjdev.tvapp.sync.SyncService.Companion.createSyncAccount
-import org.mjdev.tvlib.extensions.GlobalExt.postDelayed
-import timber.log.Timber
-
-@Suppress("unused")
-class SyncAdapterInitializer : Initializer<Unit> {
-
-    override fun create(context: Context) {
-        Timber.d("Sync started.")
-        context.createSyncAccount()?.also { account ->
-            postDelayed(20000) {
-                SyncService.requestSync(account)
-            }
-        }
-    }
-
-    override fun dependencies(): List<Class<out Initializer<*>>> {
-        return emptyList()
-    }
-
-}

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/sync/Authenticator.kt

@@ -6,7 +6,9 @@ import android.accounts.AccountAuthenticatorResponse
 import android.accounts.NetworkErrorException
 import android.accounts.NetworkErrorException
 import android.content.Context
 import android.content.Context
 import android.os.Bundle
 import android.os.Bundle
+import androidx.annotation.Keep
 
 
+@Keep
 class Authenticator(
 class Authenticator(
     context: Context
     context: Context
 ) : AbstractAccountAuthenticator(context) {
 ) : AbstractAccountAuthenticator(context) {

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/sync/AuthenticatorService.kt

@@ -3,7 +3,9 @@ package org.mjdev.tvapp.sync
 import android.app.Service
 import android.app.Service
 import android.content.Intent
 import android.content.Intent
 import android.os.IBinder
 import android.os.IBinder
+import androidx.annotation.Keep
 
 
+@Keep
 class AuthenticatorService : Service() {
 class AuthenticatorService : Service() {
 
 
     private lateinit var authenticator: Authenticator
     private lateinit var authenticator: Authenticator

+ 2 - 0
app/src/main/kotlin/org/mjdev/tvapp/sync/DataProvider.kt

@@ -5,12 +5,14 @@ import android.content.ContentValues
 import android.content.UriMatcher
 import android.content.UriMatcher
 import android.database.Cursor
 import android.database.Cursor
 import android.net.Uri
 import android.net.Uri
+import androidx.annotation.Keep
 import com.squareup.moshi.Moshi
 import com.squareup.moshi.Moshi
 import org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvapp.data.local.Movie
 import org.mjdev.tvapp.data.local.Movie
 import org.mjdev.tvapp.database.DAO
 import org.mjdev.tvapp.database.DAO
 import javax.inject.Inject
 import javax.inject.Inject
 
 
+@Keep
 @Suppress("MemberVisibilityCanBePrivate")
 @Suppress("MemberVisibilityCanBePrivate")
 class DataProvider : ContentProvider() {
 class DataProvider : ContentProvider() {
 
 

+ 4 - 0
app/src/main/kotlin/org/mjdev/tvapp/sync/SyncAdapter.kt

@@ -6,7 +6,9 @@ import android.content.ContentProviderClient
 import android.content.Context
 import android.content.Context
 import android.content.SyncResult
 import android.content.SyncResult
 import android.os.Bundle
 import android.os.Bundle
+import androidx.annotation.Keep
 import io.objectbox.Box
 import io.objectbox.Box
+import kotlinx.coroutines.delay
 import org.mjdev.tvapp.data.local.Movie
 import org.mjdev.tvapp.data.local.Movie
 import org.mjdev.tvapp.database.DAO
 import org.mjdev.tvapp.database.DAO
 import org.mjdev.tvapp.database.DAO.Companion.tx
 import org.mjdev.tvapp.database.DAO.Companion.tx
@@ -17,6 +19,7 @@ import org.mjdev.tvlib.extensions.GlobalExt.safeGet
 import org.mjdev.tvlib.interfaces.ItemWithUri.Companion.hasUri
 import org.mjdev.tvlib.interfaces.ItemWithUri.Companion.hasUri
 import timber.log.Timber
 import timber.log.Timber
 
 
+@Keep
 @Suppress("unused")
 @Suppress("unused")
 class SyncAdapter(
 class SyncAdapter(
     context: Context,
     context: Context,
@@ -40,6 +43,7 @@ class SyncAdapter(
         syncResult: SyncResult?
         syncResult: SyncResult?
     ) {
     ) {
         launch(IO) {
         launch(IO) {
+            delay(4000L)
             try {
             try {
 
 
                 val streams = apiService.streams().safeGet()
                 val streams = apiService.streams().safeGet()

+ 3 - 0
app/src/main/kotlin/org/mjdev/tvapp/sync/SyncService.kt

@@ -10,6 +10,7 @@ import android.content.Context
 import android.content.Intent
 import android.content.Intent
 import android.os.Bundle
 import android.os.Bundle
 import android.os.IBinder
 import android.os.IBinder
+import androidx.annotation.Keep
 import dagger.hilt.android.AndroidEntryPoint
 import dagger.hilt.android.AndroidEntryPoint
 import org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvapp.database.DAO
 import org.mjdev.tvapp.database.DAO
@@ -17,6 +18,7 @@ import org.mjdev.tvapp.repository.ApiService
 import timber.log.Timber
 import timber.log.Timber
 import javax.inject.Inject
 import javax.inject.Inject
 
 
+@Keep
 @AndroidEntryPoint
 @AndroidEntryPoint
 class SyncService : Service() {
 class SyncService : Service() {
 
 
@@ -51,6 +53,7 @@ class SyncService : Service() {
         private var sSyncAdapter: SyncAdapter? = null
         private var sSyncAdapter: SyncAdapter? = null
         private val sSyncAdapterLock = Any()
         private val sSyncAdapterLock = Any()
 
 
+        @Suppress("unused")
         fun Context.createSyncAccount(): Account? {
         fun Context.createSyncAccount(): Account? {
             return try {
             return try {
                 val accountManager = getSystemService(Context.ACCOUNT_SERVICE) as AccountManager
                 val accountManager = getSystemService(Context.ACCOUNT_SERVICE) as AccountManager

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

@@ -24,8 +24,8 @@ import org.mjdev.tvapp.BuildConfig
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.R
 import org.mjdev.tvapp.activity.IPTVActivity
 import org.mjdev.tvapp.activity.IPTVActivity
 import org.mjdev.tvapp.activity.IPTVActivity.Companion.IPTV_DATA
 import org.mjdev.tvapp.activity.IPTVActivity.Companion.IPTV_DATA
-import org.mjdev.tvapp.data.local.User
-import org.mjdev.tvapp.helpers.AuthManager.Companion.rememberAuthManager
+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.extensions.HiltExt.appViewModel
 import org.mjdev.tvlib.interfaces.ItemWithId
 import org.mjdev.tvlib.interfaces.ItemWithId
 import org.mjdev.tvlib.ui.components.page.Page
 import org.mjdev.tvlib.ui.components.page.Page
@@ -96,7 +96,9 @@ class MainPage : Page() {
         }
         }
 
 
         val user = remember { mutableStateOf<User?>(null) }
         val user = remember { mutableStateOf<User?>(null) }
-        val authManager = rememberAuthManager { u -> user.value = u }
+        val authManager = rememberAuthManager(
+            allowedPackages = listOf(BuildConfig.APPLICATION_ID)
+        ) { u -> user.value = u }
         val apps = remember { viewModel.apps }.collectAsState(emptyList())
         val apps = remember { viewModel.apps }.collectAsState(emptyList())
 
 
         Box(
         Box(

+ 4 - 1
tvlib/build.gradle.kts

@@ -98,7 +98,7 @@ android {
 
 
 dependencies {
 dependencies {
     // startups
     // startups
-    implementation("androidx.startup:startup-runtime:1.1.1")
+//    implementation("androidx.startup:startup-runtime:1.1.1")
     // base libs
     // base libs
     implementation("androidx.core:core-ktx:1.12.0")
     implementation("androidx.core:core-ktx:1.12.0")
     implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
     implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
@@ -148,6 +148,9 @@ dependencies {
     implementation("com.google.zxing:core:3.5.2")
     implementation("com.google.zxing:core:3.5.2")
     // svg
     // svg
     implementation("com.caverock:androidsvg-aar:1.4")
     implementation("com.caverock:androidsvg-aar:1.4")
+    // oauth
+    implementation("com.auth0.android:auth0:2.10.2")
+    implementation("com.auth0.android:jwtdecode:2.0.2")
     // debug
     // debug
     implementation("com.jakewharton.timber:timber:5.0.1")
     implementation("com.jakewharton.timber:timber:5.0.1")
     // dagger core
     // dagger core

+ 24 - 17
app/src/main/kotlin/org/mjdev/tvapp/helpers/AuthManager.kt → tvlib/src/main/kotlin/org/mjdev/tvlib/auth/AuthManager.kt

@@ -6,7 +6,7 @@
  *  w: https://mjdev.org
  *  w: https://mjdev.org
  */
  */
 
 
-package org.mjdev.tvapp.helpers
+package org.mjdev.tvlib.auth
 
 
 import android.annotation.SuppressLint
 import android.annotation.SuppressLint
 import android.content.Context
 import android.content.Context
@@ -21,27 +21,22 @@ import com.auth0.android.provider.BrowserPicker
 import com.auth0.android.provider.CustomTabsOptions
 import com.auth0.android.provider.CustomTabsOptions
 import com.auth0.android.provider.WebAuthProvider
 import com.auth0.android.provider.WebAuthProvider
 import com.auth0.android.result.Credentials
 import com.auth0.android.result.Credentials
-import org.mjdev.tvapp.BuildConfig
-import org.mjdev.tvapp.R
-import org.mjdev.tvapp.data.local.User
+import org.mjdev.tvlib.R
+import org.mjdev.tvlib.data.local.User
 import timber.log.Timber
 import timber.log.Timber
 
 
 @Suppress("unused")
 @Suppress("unused")
 class AuthManager(
 class AuthManager(
     val context: Context,
     val context: Context,
+    val scheme: String = context.getString(R.string.com_auth0_scheme),
+    val clientId: String = context.getString(R.string.com_auth0_client_id),
+    val domain: String = context.getString(R.string.com_auth0_domain),
+    val allowedPackages: List<String> = listOf(), // BuildConfig.APPLICATION_ID
     val onUserChange: AuthManager.(user: User?) -> Unit = {}
     val onUserChange: AuthManager.(user: User?) -> Unit = {}
 ) {
 ) {
 
 
     var idToken: String? = null
     var idToken: String? = null
-
     val isUserLoggedIn: Boolean get() = ((idToken != null) && (user != null))
     val isUserLoggedIn: Boolean get() = ((idToken != null) && (user != null))
-
-    private val scheme get() = context.getString(R.string.com_auth0_scheme)
-
-    private val clientId get() = context.getString(R.string.com_auth0_client_id)
-
-    private val domain get() = context.getString(R.string.com_auth0_domain)
-
     private val account: Auth0 by lazy { Auth0(clientId, domain) }
     private val account: Auth0 by lazy { Auth0(clientId, domain) }
 
 
     val user: User?
     val user: User?
@@ -67,7 +62,8 @@ class AuthManager(
         }
         }
 
 
     init {
     init {
-        idToken = context.getSharedPreferences(SAVE_KEY, Context.MODE_PRIVATE)
+        idToken = context
+            .getSharedPreferences(SAVE_KEY, Context.MODE_PRIVATE)
             .getString(SAVE_KEY, null)
             .getString(SAVE_KEY, null)
         onUserChange(this, user)
         onUserChange(this, user)
     }
     }
@@ -88,7 +84,7 @@ class AuthManager(
                     .showTitle(false)
                     .showTitle(false)
                     .withBrowserPicker(
                     .withBrowserPicker(
                         BrowserPicker.newBuilder()
                         BrowserPicker.newBuilder()
-                            .withAllowedPackages(listOf(BuildConfig.APPLICATION_ID))
+                            .withAllowedPackages(allowedPackages)
                             .build()
                             .build()
                     )
                     )
                     .build()
                     .build()
@@ -123,7 +119,7 @@ class AuthManager(
                     .showTitle(false)
                     .showTitle(false)
                     .withBrowserPicker(
                     .withBrowserPicker(
                         BrowserPicker.newBuilder()
                         BrowserPicker.newBuilder()
-                            .withAllowedPackages(listOf(BuildConfig.APPLICATION_ID))
+                            .withAllowedPackages(allowedPackages)
                             .build()
                             .build()
                     )
                     )
                     .build()
                     .build()
@@ -151,10 +147,21 @@ class AuthManager(
         @SuppressLint("RememberReturnType", "ComposableNaming")
         @SuppressLint("RememberReturnType", "ComposableNaming")
         @Composable
         @Composable
         fun rememberAuthManager(
         fun rememberAuthManager(
-            onUserChange: AuthManager.(user: User?) -> Unit
+            scheme: String? = null,
+            clientId: String? = null,
+            domain: String? = null,
+            allowedPackages: List<String> = listOf(),
+            onUserChange: AuthManager.(user: User?) -> Unit = {}
         ) = LocalContext.current.let { ctx ->
         ) = LocalContext.current.let { ctx ->
             remember(ctx) {
             remember(ctx) {
-                AuthManager(ctx, onUserChange)
+                AuthManager(
+                    context = ctx,
+                    scheme = scheme ?: ctx.getString(R.string.com_auth0_scheme),
+                    clientId = clientId ?: ctx.getString(R.string.com_auth0_client_id),
+                    domain = domain ?: ctx.getString(R.string.com_auth0_domain),
+                    allowedPackages = allowedPackages,
+                    onUserChange = onUserChange
+                )
             }
             }
         }
         }
 
 

+ 3 - 5
app/src/main/kotlin/org/mjdev/tvapp/data/local/User.kt → tvlib/src/main/kotlin/org/mjdev/tvlib/data/local/User.kt

@@ -6,18 +6,16 @@
  *  w: https://mjdev.org
  *  w: https://mjdev.org
  */
  */
 
 
-package org.mjdev.tvapp.data.local
+package org.mjdev.tvlib.data.local
 
 
 import com.squareup.moshi.Json
 import com.squareup.moshi.Json
 import com.squareup.moshi.JsonClass
 import com.squareup.moshi.JsonClass
-import io.objectbox.annotation.Entity
-import io.objectbox.annotation.Id
 
 
-@Entity
+//@Entity
 @JsonClass(generateAdapter = true)
 @JsonClass(generateAdapter = true)
 class User {
 class User {
 
 
-    @Id
+//    @Id
     @Json(name = "id")
     @Json(name = "id")
     var id: Long = 0
     var id: Long = 0
 
 

+ 32 - 0
tvlib/src/main/kotlin/org/mjdev/tvlib/database/convert/CustomPropertyConverter.kt

@@ -0,0 +1,32 @@
+///*
+// *  Copyright (c) Milan Jurkulák 2023.
+// *  Contact:
+// *  e: mimoccc@gmail.com
+// *  e: mj@mjdev.org
+// *  w: https://mjdev.org
+// */
+//
+//@file:Suppress("unused")
+//
+//package org.mjdev.tvlib.database.convert
+//
+//import io.objectbox.converter.PropertyConverter
+//
+//open class ConvertEntity : Any() {
+//    open val id: Long = 0L
+//}
+//
+//open class CustomPropertyConverter<T : ConvertEntity>(
+//
+//    private val supplier: (id: Long) -> T?
+//
+//) : PropertyConverter<T, Long> {
+//
+//    override fun convertToEntityProperty(databaseValue: Long?): T? =
+//        if (databaseValue == null) null
+//        else supplier.invoke(databaseValue)
+//
+//    override fun convertToDatabaseValue(entityProperty: T): Long =
+//        entityProperty.id
+//
+//}

+ 30 - 0
tvlib/src/main/kotlin/org/mjdev/tvlib/database/convert/EncryptionConverter.kt

@@ -0,0 +1,30 @@
+///*
+// *  Copyright (c) Milan Jurkulák 2023.
+// *  Contact:
+// *  e: mimoccc@gmail.com
+// *  e: mj@mjdev.org
+// *  w: https://mjdev.org
+// */
+//
+//@file:Suppress("unused")
+//
+//package org.mjdev.tvlib.database.convert
+//
+//import com.scottyab.aescrypt.AESCrypt
+//import io.objectbox.converter.PropertyConverter
+//
+//class EncryptionConverter : PropertyConverter<String, String> {
+//
+//    companion object {
+//
+//        const val AES_KEY = "simply key to encrypt data"
+//
+//    }
+//
+//    override fun convertToDatabaseValue(entityProperty: String): String =
+//        AESCrypt.encrypt(AES_KEY, entityProperty)
+//
+//    override fun convertToEntityProperty(databaseValue: String?): String =
+//        AESCrypt.decrypt(AES_KEY, databaseValue)
+//
+//}

+ 31 - 0
tvlib/src/main/kotlin/org/mjdev/tvlib/database/convert/PropertyEnumConverter.kt

@@ -0,0 +1,31 @@
+/*
+ *  Copyright (c) Milan Jurkulák 2023.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+//package org.mjdev.tvlib.database.convert
+//
+//import io.objectbox.converter.PropertyConverter
+//
+//@Suppress("unused")
+//open class PropertyEnumConverter<T, R>(
+//
+//    private val values: Array<T>,
+//    private val default: T,
+//    private val predicate: T.(R) -> Boolean,
+//    private val supplier: (T) -> R?
+//
+//) : PropertyConverter<T, R> {
+//
+//    override fun convertToEntityProperty(databaseValue: R?): T? =
+//        if (databaseValue == null) null
+//        else values.firstOrNull { p -> p.predicate(databaseValue) } ?: default
+//
+//    override fun convertToDatabaseValue(entityProperty: T?): R? =
+//        if (entityProperty == null) null
+//        else supplier(entityProperty)
+//
+//}

+ 29 - 0
tvlib/src/main/kotlin/org/mjdev/tvlib/database/convert/PropertyJsonConverter.kt

@@ -0,0 +1,29 @@
+/*
+ *  Copyright (c) Milan Jurkulák 2023.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+//package org.mjdev.tvlib.database.convert
+//
+//import com.squareup.moshi.Moshi
+//import io.objectbox.converter.PropertyConverter
+//
+//@Suppress("unused")
+//open class PropertyJsonConverter<T : Any>(
+//
+//    private val cls: Class<T>
+//
+//    ) : PropertyConverter<T, String> {
+//
+//    override fun convertToEntityProperty(databaseValue: String?): T? =
+//        if (databaseValue == null) null
+//        else Moshi.Builder().build().adapter(cls).fromJson(databaseValue) as T
+//
+//    override fun convertToDatabaseValue(entityProperty: T?): String? =
+//        if (entityProperty == null) null
+//        else Moshi.Builder().build().adapter(cls).toJson(entityProperty)
+//
+//}

+ 41 - 0
tvlib/src/main/kotlin/org/mjdev/tvlib/database/convert/StringListConverter.kt

@@ -0,0 +1,41 @@
+/*
+ *  Copyright (c) Milan Jurkulák 2023.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+//package org.mjdev.tvlib.database.convert
+//
+//import io.objectbox.converter.PropertyConverter
+//import org.json.JSONArray
+//
+//@Suppress("unused")
+//class StringListConverter : PropertyConverter<List<String?>?, String?> {
+//
+//    override fun convertToEntityProperty(
+//        databaseValue: String?
+//    ): List<String?> = if (databaseValue == null) ArrayList()
+//    else try {
+//        val array = JSONArray(databaseValue)
+//        val ret = ArrayList<String?>()
+//        for (i in 0 until array.length()) {
+//            ret.add(array.getString(i))
+//        }
+//        ret
+//    } catch (e: Exception) {
+//        ArrayList()
+//    }
+//
+//    override fun convertToDatabaseValue(
+//        entityProperty: List<String?>?
+//    ): String? {
+//        return try {
+//            if (entityProperty == null) null else JSONArray(entityProperty).toString()
+//        } catch (e: Exception) {
+//            null
+//        }
+//    }
+//
+//}

+ 44 - 14
tvlib/src/main/kotlin/org/mjdev/tvlib/extensions/NavGraphBuilderExt.kt

@@ -18,27 +18,57 @@ import org.mjdev.tvlib.extensions.NavBackStackEntryExt.arg
 import org.mjdev.tvlib.navigation.NavGraphBuilderEx
 import org.mjdev.tvlib.navigation.NavGraphBuilderEx
 import org.mjdev.tvlib.screen.Screen
 import org.mjdev.tvlib.screen.Screen
 
 
+typealias AnimType<T> = (@JvmSuppressWildcards AnimatedContentTransitionScope<NavBackStackEntry>.() -> T?)
+
 object NavGraphBuilderExt {
 object NavGraphBuilderExt {
 
 
+    fun <T : Screen> NavGraphBuilderEx.startScreen(
+        route: T,
+        isHomeScreen: Boolean = false,
+        deepLinks: List<NavDeepLink> = emptyList(),
+        enterTransition: AnimType<EnterTransition>? = { AnimExt.FadeIn },
+        exitTransition: AnimType<ExitTransition>? = { AnimExt.FadeOut },
+        popEnterTransition: AnimType<EnterTransition>? = enterTransition,
+        popExitTransition: AnimType<ExitTransition>? = exitTransition
+    ) = screen(
+        route = route,
+        isStartScreen = true,
+        isHomeScreen = isHomeScreen,
+        deepLinks = deepLinks,
+        enterTransition = enterTransition,
+        exitTransition = exitTransition,
+        popEnterTransition = popEnterTransition,
+        popExitTransition = popExitTransition
+    )
+
+    fun <T : Screen> NavGraphBuilderEx.homeScreen(
+        route: T,
+        isStartScreen: Boolean = false,
+        deepLinks: List<NavDeepLink> = emptyList(),
+        enterTransition: AnimType<EnterTransition>? = { AnimExt.FadeIn },
+        exitTransition: AnimType<ExitTransition>? = { AnimExt.FadeOut },
+        popEnterTransition: AnimType<EnterTransition>? = enterTransition,
+        popExitTransition: AnimType<ExitTransition>? = exitTransition
+    ) = screen(
+        route = route,
+        isStartScreen = isStartScreen,
+        isHomeScreen = true,
+        deepLinks = deepLinks,
+        enterTransition = enterTransition,
+        exitTransition = exitTransition,
+        popEnterTransition = popEnterTransition,
+        popExitTransition = popExitTransition
+    )
+
     fun <T : Screen> NavGraphBuilderEx.screen(
     fun <T : Screen> NavGraphBuilderEx.screen(
         route: T,
         route: T,
         isHomeScreen: Boolean = false,
         isHomeScreen: Boolean = false,
         isStartScreen: Boolean = false,
         isStartScreen: Boolean = false,
         deepLinks: List<NavDeepLink> = emptyList(),
         deepLinks: List<NavDeepLink> = emptyList(),
-        enterTransition: (@JvmSuppressWildcards
-        AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition?)? = {
-            AnimExt.FadeIn
-        },
-        exitTransition: (@JvmSuppressWildcards
-        AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition?)? = {
-            AnimExt.FadeOut
-        },
-        popEnterTransition: (@JvmSuppressWildcards
-        AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition?)? =
-            enterTransition,
-        popExitTransition: (@JvmSuppressWildcards
-        AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition?)? =
-            exitTransition,
+        enterTransition: AnimType<EnterTransition>? = { AnimExt.FadeIn },
+        exitTransition: AnimType<ExitTransition>? = { AnimExt.FadeOut },
+        popEnterTransition: AnimType<EnterTransition>? = enterTransition,
+        popExitTransition: AnimType<ExitTransition>? = exitTransition
     ) {
     ) {
         if (isStartScreen) {
         if (isStartScreen) {
             splashDestinationRoute = route.route
             splashDestinationRoute = route.route

+ 3 - 1
tvlib/src/main/kotlin/org/mjdev/tvlib/glide/ApplicationGlideApp.kt

@@ -32,6 +32,7 @@ import org.mjdev.tvlib.R
 import org.mjdev.tvlib.glide.sources.albumart.AlbumArtLoaderFactory
 import org.mjdev.tvlib.glide.sources.albumart.AlbumArtLoaderFactory
 import org.mjdev.tvlib.glide.sources.layout.LayoutLoaderFactory
 import org.mjdev.tvlib.glide.sources.layout.LayoutLoaderFactory
 import org.mjdev.tvlib.glide.sources.layout.LayoutResId
 import org.mjdev.tvlib.glide.sources.layout.LayoutResId
+import org.mjdev.tvlib.glide.sources.m3u.M3ULoaderFactory
 import org.mjdev.tvlib.glide.sources.svg.SvgDecoder
 import org.mjdev.tvlib.glide.sources.svg.SvgDecoder
 import org.mjdev.tvlib.glide.sources.svg.SvgDrawableTranscoder
 import org.mjdev.tvlib.glide.sources.svg.SvgDrawableTranscoder
 import java.io.InputStream
 import java.io.InputStream
@@ -106,7 +107,8 @@ class ApplicationGlideApp : AppGlideModule() {
             // LayoutRes support (create images from layout res Id)
             // LayoutRes support (create images from layout res Id)
             prepend(LayoutResId::class.java, Drawable::class.java, LayoutLoaderFactory(context))
             prepend(LayoutResId::class.java, Drawable::class.java, LayoutLoaderFactory(context))
             // Album Art audio files
             // Album Art audio files
-            append(String::class.java, InputStream::class.java, AlbumArtLoaderFactory(context))
+            prepend(String::class.java, InputStream::class.java, AlbumArtLoaderFactory(context))
+            prepend(String::class.java, InputStream::class.java, M3ULoaderFactory(context))
             // SVG support
             // SVG support
             register(SVG::class.java, PictureDrawable::class.java, SvgDrawableTranscoder())
             register(SVG::class.java, PictureDrawable::class.java, SvgDrawableTranscoder())
             append(InputStream::class.java, SVG::class.java, SvgDecoder())
             append(InputStream::class.java, SVG::class.java, SvgDecoder())

+ 78 - 0
tvlib/src/main/kotlin/org/mjdev/tvlib/glide/sources/m3u/M3ULoaderFactory.kt

@@ -0,0 +1,78 @@
+/*
+ *  Copyright (c) Milan Jurkulák 2023.
+ *  Contact:
+ *  e: mimoccc@gmail.com
+ *  e: mj@mjdev.org
+ *  w: https://mjdev.org
+ */
+
+package org.mjdev.tvlib.glide.sources.m3u
+
+import android.content.Context
+import com.bumptech.glide.Priority
+import com.bumptech.glide.load.DataSource
+import com.bumptech.glide.load.Options
+import com.bumptech.glide.load.data.DataFetcher
+import com.bumptech.glide.load.model.ModelLoader
+import com.bumptech.glide.load.model.ModelLoaderFactory
+import com.bumptech.glide.load.model.MultiModelLoaderFactory
+import com.bumptech.glide.signature.ObjectKey
+import java.io.InputStream
+
+// disable m3u loading
+@Suppress("UNCHECKED_CAST", "PrivatePropertyName")
+class M3ULoaderFactory(
+    private val context: Context
+) : ModelLoader<String?, InputStream?>, ModelLoaderFactory<String?, InputStream?> {
+
+    override fun buildLoadData(
+        source: String,
+        width: Int,
+        height: Int,
+        options: Options
+    ): ModelLoader.LoadData<InputStream?> =
+        ModelLoader.LoadData<InputStream?>(ObjectKey(source), CoverFetcher(source))
+
+    override fun handles(
+        model: String
+    ): Boolean = model.isM3UFileOrUrl()
+
+    override fun build(
+        multiFactory: MultiModelLoaderFactory
+    ): ModelLoader<String?, InputStream?> = M3ULoaderFactory(context)
+
+    override fun teardown() {
+    }
+
+    inner class CoverFetcher(
+        private val source: String?
+    ) : DataFetcher<InputStream?> {
+
+        override fun loadData(
+            priority: Priority,
+            callback: DataFetcher.DataCallback<in InputStream?>
+        ) {
+            callback.onLoadFailed(Exception("No cover"))
+        }
+
+        override fun cleanup() {
+        }
+
+        override fun cancel() {
+        }
+
+        override fun getDataClass(): Class<InputStream?> {
+            return InputStream::class.java as Class<InputStream?>
+        }
+
+        override fun getDataSource(): DataSource {
+            return DataSource.LOCAL
+        }
+
+    }
+
+}
+
+private fun String.isM3UFileOrUrl(): Boolean = trim().let {
+    it.endsWith("m3u") || it.endsWith("m3u8")
+}

+ 2 - 3
tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/tv/NeonRectangle.kt

@@ -20,8 +20,6 @@ import androidx.compose.ui.graphics.toArgb
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.DpSize
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.dp
-import androidx.tv.material3.CardDefaults
-import androidx.tv.material3.CardGlow
 import androidx.tv.material3.ExperimentalTvMaterial3Api
 import androidx.tv.material3.ExperimentalTvMaterial3Api
 import org.mjdev.tvlib.annotations.Previews
 import org.mjdev.tvlib.annotations.Previews
 
 
@@ -92,11 +90,12 @@ fun NeonRectangle(
     }
     }
 }
 }
 
 
+@Suppress("unused")
 @OptIn(ExperimentalTvMaterial3Api::class)
 @OptIn(ExperimentalTvMaterial3Api::class)
 @Composable
 @Composable
 fun NeonRectangle(
 fun NeonRectangle(
     modifier: Modifier = Modifier,
     modifier: Modifier = Modifier,
-    glow: CardGlow = CardDefaults.glow()
+//    glow: CardGlow = CardDefaults.glow()
 ) {
 ) {
     // todo
     // todo
     val colorRect: Color = Color.Green
     val colorRect: Color = Color.Green

+ 5 - 4
tvlib/src/main/kotlin/org/mjdev/tvlib/ui/components/web/WebView.kt

@@ -31,7 +31,6 @@ import org.mjdev.tvlib.annotations.Previews
 import org.mjdev.tvlib.extensions.ComposeExt.isEditMode
 import org.mjdev.tvlib.extensions.ComposeExt.isEditMode
 import timber.log.Timber
 import timber.log.Timber
 
 
-
 private const val USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) " +
 private const val USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) " +
         "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
         "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36"
 
 
@@ -50,7 +49,8 @@ fun WebView(
         factory = { context ->
         factory = { context ->
             CustomWebView(
             CustomWebView(
                 context = context,
                 context = context,
-                inEditMode = isEdit
+                inEditMode = isEdit,
+                enableJavaScript = enableJavaScript
             )
             )
         },
         },
         update = { view -> view.loadUrl(url) }
         update = { view -> view.loadUrl(url) }
@@ -62,7 +62,8 @@ class CustomWebView(
     context: Context,
     context: Context,
     attrs: AttributeSet? = null,
     attrs: AttributeSet? = null,
     defStyleAttr: Int = 0,
     defStyleAttr: Int = 0,
-    val inEditMode: Boolean = false
+    val inEditMode: Boolean = false,
+    val enableJavaScript: Boolean = true
 ) : WebView(context, attrs, defStyleAttr) {
 ) : WebView(context, attrs, defStyleAttr) {
     private val cookieManager: CookieManager by lazy { CookieManager.getInstance() }
     private val cookieManager: CookieManager by lazy { CookieManager.getInstance() }
 
 
@@ -77,7 +78,7 @@ class CustomWebView(
             settings.domStorageEnabled = true
             settings.domStorageEnabled = true
             settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
             settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
             settings.useWideViewPort = true
             settings.useWideViewPort = true
-            settings.javaScriptEnabled = true
+            settings.javaScriptEnabled = enableJavaScript
             settings.javaScriptCanOpenWindowsAutomatically = true
             settings.javaScriptCanOpenWindowsAutomatically = true
             settings.allowContentAccess = true
             settings.allowContentAccess = true
             settings.allowFileAccess = true
             settings.allowFileAccess = true

+ 3 - 0
tvlib/src/main/res/values/strings.xml

@@ -42,5 +42,8 @@
     <string name="title_audio_local">Local audio files</string>
     <string name="title_audio_local">Local audio files</string>
     <string name="title_video_local">Local video files</string>
     <string name="title_video_local">Local video files</string>
     <string name="title_photo_local">Local photo files</string>
     <string name="title_photo_local">Local photo files</string>
+    <string name="com_auth0_client_id" />
+    <string name="com_auth0_scheme" />
+    <string name="com_auth0_domain" />
 
 
 </resources>
 </resources>