From a13ffc945523a96a7adf70efd269106aee3fd238 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 20:15:35 +0000 Subject: [PATCH 001/122] Add tests for compose --- composeApp/build.gradle.kts | 4 + .../preferences/PreferenceDefaultsTest.kt | 31 +++++ .../preferences/PreferenceKeysTest.kt | 33 +++++ .../ui/theme/CloudStreamColorSchemeTest.kt | 114 ++++++++++++++++++ .../ui/theme/CloudStreamPaletteTest.kt | 57 +++++++++ .../ui/theme/CloudStreamPrimaryColorTest.kt | 48 ++++++++ .../ui/theme/CloudStreamThemeModeTest.kt | 35 ++++++ .../cloudstream4/utils/DeviceLayoutTest.kt | 68 +++++++++++ 8 files changed, 390 insertions(+) create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceDefaultsTest.kt create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPaletteTest.kt create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPrimaryColorTest.kt create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeModeTest.kt create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 276a6945bdf..7feea0b62c9 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -47,6 +47,10 @@ kotlin { implementation(libs.activity.compose) implementation(libs.preference.ktx) } + + commonTest.dependencies { + implementation(libs.kotlin.test) + } } } diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceDefaultsTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceDefaultsTest.kt new file mode 100644 index 00000000000..dc71d5ecacd --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceDefaultsTest.kt @@ -0,0 +1,31 @@ +package com.lagradost.cloudstream4.preferences + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals + +class PreferenceDefaultsTest { + + @Test + fun appThemeDefaultsToAmoledLight() { + assertEquals("AmoledLight", PreferenceDefaults.APP_THEME) + } + + @Test + fun primaryColorDefaultsToNormal() { + assertEquals("Normal", PreferenceDefaults.PRIMARY_COLOR) + } + + @Test + fun appLayoutDefaultsToAuto() { + // -1 is "Auto" + assertEquals(-1, PreferenceDefaults.APP_LAYOUT) + } + + @Test + fun appLayoutDoesNotCollideWithAnyExplicitLayoutValue() { + // Explicit layout values are 0 (Phone), 1 (TV), 2 (Emulator). + val explicitLayoutValues = setOf(0, 1, 2) + assertNotEquals(true, explicitLayoutValues.contains(PreferenceDefaults.APP_LAYOUT)) + } +} diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt new file mode 100644 index 00000000000..c97f6774353 --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt @@ -0,0 +1,33 @@ +package com.lagradost.cloudstream4.preferences + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class PreferenceKeysTest { + + @Test + fun appLayoutKeyHasExpectedValue() { + assertEquals("app_layout_key", PreferenceKeys.APP_LAYOUT) + } + + @Test + fun appThemeKeyHasExpectedValue() { + assertEquals("app_theme_key", PreferenceKeys.APP_THEME) + } + + @Test + fun primaryColorKeyHasExpectedValue() { + assertEquals("primary_color_key", PreferenceKeys.PRIMARY_COLOR) + } + + @Test + fun allPreferenceKeysAreUnique() { + val keys = listOf( + PreferenceKeys.APP_LAYOUT, + PreferenceKeys.APP_THEME, + PreferenceKeys.PRIMARY_COLOR, + ) + assertTrue(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") + } +} diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt new file mode 100644 index 00000000000..4139223ffdc --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt @@ -0,0 +1,114 @@ +package com.lagradost.cloudstream4.ui.theme + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class CloudStreamColorSchemeTest { + + @Test + fun darkSchemeIsNotLight() { + assertFalse(darkScheme().isLight) + } + + @Test + fun lightSchemeIsLight() { + assertTrue(lightScheme().isLight) + } + + @Test + fun amoledSchemeIsDerivedFromDarkSchemeButNotLight() { + assertFalse(amoledScheme().isLight) + } + + @Test + fun amoledSchemeOverridesAllBackgroundLayersToPureBlack() { + val amoled = amoledScheme() + assertEquals(CloudStreamPalette.AmoledBlack, amoled.background) + assertEquals(CloudStreamPalette.AmoledBlack, amoled.surface) + assertEquals(CloudStreamPalette.AmoledBlack, amoled.surfaceVariant) + assertEquals(CloudStreamPalette.AmoledBlack, amoled.surfaceContainer) + } + + @Test + fun amoledSchemeKeepsDarkSchemeTextAndIconColors() { + // Only backgrounds are overridden on top of darkScheme(); text/icon/primary + // should be carried over unchanged. + val dark = darkScheme() + val amoled = amoledScheme() + assertEquals(dark.onBackground, amoled.onBackground) + assertEquals(dark.onSurfaceVariant, amoled.onSurfaceVariant) + assertEquals(dark.icon, amoled.icon) + assertEquals(dark.primary, amoled.primary) + } + + @Test + fun draculaSchemeIsDark() { + assertFalse(draculaScheme().isLight) + } + + @Test + fun lavenderSchemeIsLight() { + assertTrue(lavenderScheme().isLight) + } + + @Test + fun silentBlueSchemeIsDark() { + assertFalse(silentBlueScheme().isLight) + } + + @Test + fun allNamedSchemesSharePrimaryAndOngoingColors() { + // Every preset (aside from copies that explicitly override it) shares the + // same brand primary/ongoing accent colors from the palette. + val schemes = listOf( + darkScheme(), lightScheme(), draculaScheme(), lavenderScheme(), silentBlueScheme(), + ) + for (scheme in schemes) { + assertEquals(CloudStreamPalette.Primary, scheme.primary) + assertEquals(CloudStreamPalette.Ongoing, scheme.ongoing) + } + } + + @Test + fun copyOverridesOnlyRequestedFieldsAndPreservesTheRest() { + val original = darkScheme() + val copy = original.copy(primary = CloudStreamPalette.Ongoing) + + assertEquals(CloudStreamPalette.Ongoing, copy.primary) + assertEquals(original.background, copy.background) + assertEquals(original.surface, copy.surface) + assertEquals(original.onBackground, copy.onBackground) + assertEquals(original.isLight, copy.isLight) + } + + @Test + fun copyWithNoArgumentsProducesAnEquivalentButDistinctInstance() { + val original = lightScheme() + val copy = original.copy() + + assertEquals(original.background, copy.background) + assertEquals(original.surface, copy.surface) + assertEquals(original.surfaceVariant, copy.surfaceVariant) + assertEquals(original.surfaceContainer, copy.surfaceContainer) + assertEquals(original.onBackground, copy.onBackground) + assertEquals(original.onSurfaceVariant, copy.onSurfaceVariant) + assertEquals(original.icon, copy.icon) + assertEquals(original.primary, copy.primary) + assertEquals(original.ongoing, copy.ongoing) + assertEquals(original.isLight, copy.isLight) + } + + @Test + fun mutatingASchemeFieldDoesNotAffectAFreshlyBuiltScheme() { + // Fields are individually mutable (backed by mutableStateOf) to support + // recomposition; verify that mutation is instance-local. + val mutated = darkScheme() + mutated.primary = CloudStreamPalette.Ongoing + + val fresh = darkScheme() + assertEquals(CloudStreamPalette.Primary, fresh.primary) + assertEquals(CloudStreamPalette.Ongoing, mutated.primary) + } +} diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPaletteTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPaletteTest.kt new file mode 100644 index 00000000000..04c3a4f16ba --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPaletteTest.kt @@ -0,0 +1,57 @@ +package com.lagradost.cloudstream4.ui.theme + +import androidx.compose.ui.graphics.Color +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue + +class CloudStreamPaletteTest { + + @Test + fun primaryMatchesExpectedHexValue() { + assertEquals(Color(0xFF3D50FA), CloudStreamPalette.Primary) + } + + @Test + fun amoledBlackIsPureBlack() { + assertEquals(Color(0xFF000000), CloudStreamPalette.AmoledBlack) + } + + @Test + fun lightBlackBgIsPureWhite() { + assertEquals(Color(0xFFFFFFFF), CloudStreamPalette.LightBlackBg) + } + + @Test + fun amoledBlackAndAmoledNearBlackAreDistinct() { + assertNotEquals(CloudStreamPalette.AmoledBlack, CloudStreamPalette.AmoledNearBlack) + } + + @Test + fun darkBackgroundColorsAreDarkerThanLightBackgroundColors() { + // A rough sanity check that "dark" theme colors are actually darker + // (lower luminance) than their "light" theme counterparts. + fun luminance(color: Color) = (color.red + color.green + color.blue) / 3f + + assertTrue(luminance(CloudStreamPalette.DarkBlackBg) < luminance(CloudStreamPalette.LightBlackBg)) + assertTrue(luminance(CloudStreamPalette.DarkText) > luminance(CloudStreamPalette.LightText)) + } + + @Test + fun eachThemeDefinesADistinctBackgroundColor() { + val backgrounds = listOf( + CloudStreamPalette.DarkBlackBg, + CloudStreamPalette.AmoledBlack, + CloudStreamPalette.LightBlackBg, + CloudStreamPalette.DraculaBlackBg, + CloudStreamPalette.LavenderBlackBg, + CloudStreamPalette.SilentBlueBlackBg, + ) + assertEquals( + backgrounds.size, + backgrounds.toSet().size, + "Theme backgrounds should not collide: $backgrounds", + ) + } +} diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPrimaryColorTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPrimaryColorTest.kt new file mode 100644 index 00000000000..0030a0391d6 --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPrimaryColorTest.kt @@ -0,0 +1,48 @@ +package com.lagradost.cloudstream4.ui.theme + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class CloudStreamPrimaryColorTest { + + @Test + fun normalMatchesTheCloudStreamPaletteAccentColor() { + assertEquals(CloudStreamPalette.Primary, CloudStreamPrimaryColor.NORMAL.color) + } + + @Test + fun dynamicAndDynamicTwoFallBackToTheSameDefaultColor() { + // These entries are placeholders whose real color is resolved dynamically + // at runtime (see resolveDynamicPrimaryColor / resolveDynamicSecondaryColor); + // their static enum color should just be a sane, matching fallback. + assertEquals(CloudStreamPrimaryColor.DYNAMIC.color, CloudStreamPrimaryColor.DYNAMIC_TWO.color) + assertEquals(CloudStreamPrimaryColor.NORMAL.color, CloudStreamPrimaryColor.DYNAMIC.color) + } + + @Test + fun everyEntryHasAFullyOpaqueColor() { + for (entry in CloudStreamPrimaryColor.entries) { + assertEquals(1f, entry.color.alpha, "Expected $entry to be fully opaque") + } + } + + @Test + fun containsExpectedNumberOfColorOptions() { + assertEquals(22, CloudStreamPrimaryColor.entries.size) + } + + @Test + fun whiteEntryIsActuallyWhite() { + val white = CloudStreamPrimaryColor.WHITE.color + assertEquals(1f, white.red) + assertEquals(1f, white.green) + assertEquals(1f, white.blue) + } + + @Test + fun noTwoEntriesShareTheSameName() { + val names = CloudStreamPrimaryColor.entries.map { it.name } + assertTrue(names.toSet().size == names.size) + } +} diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeModeTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeModeTest.kt new file mode 100644 index 00000000000..388f0d7130d --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeModeTest.kt @@ -0,0 +1,35 @@ +package com.lagradost.cloudstream4.ui.theme + +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class CloudStreamThemeModeTest { + + @Test + fun containsExpectedNumberOfModes() { + assertEquals(8, CloudStreamThemeMode.entries.size) + } + + @Test + fun containsAllExpectedModesByName() { + val expected = setOf( + "Dark", "Amoled", "Light", "Dracula", "Lavender", "SilentBlue", "FollowSystem", "Dynamic", + ) + val actual = CloudStreamThemeMode.entries.map { it.name }.toSet() + assertEquals(expected, actual) + } + + @Test + fun noTwoModesShareAnOrdinal() { + val ordinals = CloudStreamThemeMode.entries.map { it.ordinal } + assertTrue(ordinals.toSet().size == ordinals.size) + } + + @Test + fun valueOfRoundTripsForEveryMode() { + for (mode in CloudStreamThemeMode.entries) { + assertEquals(mode, CloudStreamThemeMode.valueOf(mode.name)) + } + } +} diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt new file mode 100644 index 00000000000..b5d9e6a4eb5 --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt @@ -0,0 +1,68 @@ +package com.lagradost.cloudstream4.utils + +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +class DeviceLayoutTest { + + @Test + fun singleFlagMatchesItself() { + assertTrue((DeviceLayout.PHONE).and(DeviceLayout.PHONE)) + } + + @Test + fun singleFlagDoesNotMatchOtherFlag() { + assertFalse((DeviceLayout.PHONE).and(DeviceLayout.TV)) + } + + @Test + fun orCombinesTwoFlagsSoEitherMatches() { + val combined = DeviceLayout.PHONE or DeviceLayout.TV + assertTrue(combined.and(DeviceLayout.PHONE)) + assertTrue(combined.and(DeviceLayout.TV)) + } + + @Test + fun orCombinationDoesNotMatchAnUnrelatedFlag() { + val combined = DeviceLayout.PHONE or DeviceLayout.TV + assertFalse(combined.and(DeviceLayout.EMULATOR)) + assertFalse(combined.and(DeviceLayout.COMPUTER)) + } + + @Test + fun orIsCommutative() { + val a = DeviceLayout.PHONE or DeviceLayout.COMPUTER + val b = DeviceLayout.COMPUTER or DeviceLayout.PHONE + assertTrue(a.and(DeviceLayout.PHONE) == b.and(DeviceLayout.PHONE)) + assertTrue(a.and(DeviceLayout.COMPUTER) == b.and(DeviceLayout.COMPUTER)) + } + + @Test + fun combiningAllFourFlagsMatchesEachOne() { + val everything = + DeviceLayout.PHONE or DeviceLayout.TV or DeviceLayout.EMULATOR or DeviceLayout.COMPUTER + assertTrue(everything.and(DeviceLayout.PHONE)) + assertTrue(everything.and(DeviceLayout.TV)) + assertTrue(everything.and(DeviceLayout.EMULATOR)) + assertTrue(everything.and(DeviceLayout.COMPUTER)) + } + + @Test + fun isLayoutReflectsResolvedLayoutAfterUpdate() { + DeviceLayout.update() + // resolveLayout() always resolves to exactly one of these four flags, + // so at least one of them must be considered active. + val anyKnownLayout = + DeviceLayout.PHONE or DeviceLayout.TV or DeviceLayout.EMULATOR or DeviceLayout.COMPUTER + assertTrue(DeviceLayout.isLayout(anyKnownLayout)) + } + + @Test + fun isLandscapeDoesNotThrow() { + DeviceLayout.update() + // Just verifying this completes without throwing on every target, + // since the actual boolean result is platform/environment dependent. + DeviceLayout.isLandscape() + } +} From 3e68f629be082e3f209e56d34bf852f851f62688 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:09:23 -0600 Subject: [PATCH 002/122] Add --- gradle/libs.versions.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 681e2e092ac..b563ba46f87 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -83,6 +83,7 @@ compose-material3 = { module = "org.jetbrains.compose.material3:material3", vers compose-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" } compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeMultiplatform" } compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" } +compose-ui-test = { module = "org.jetbrains.compose.ui:ui-test", version.ref = "composeMultiplatform" } compose-ui-tooling = { module = "org.jetbrains.compose.ui:ui-tooling", version.ref = "composeMultiplatform" } compose-ui-tooling-preview = { module = "org.jetbrains.compose.ui:ui-tooling-preview", version.ref = "composeMultiplatform" } conscrypt-android = { module = "org.conscrypt:conscrypt-android", version.ref = "conscryptAndroid" } From c4032dd18a73ed5c9a8615794a74beef55ee12ac Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:10:11 -0600 Subject: [PATCH 003/122] Add --- composeApp/build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 7feea0b62c9..22f4d8c42dd 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -49,6 +49,7 @@ kotlin { } commonTest.dependencies { + implementation(libs.compose.ui.test) implementation(libs.kotlin.test) } } From 09e4659156f80af53abda9b638017485dad6f33f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:19:50 -0600 Subject: [PATCH 004/122] Add test --- .../ui/components/TvFocusableTest.kt | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt new file mode 100644 index 00000000000..0a2a48c3203 --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt @@ -0,0 +1,126 @@ +package com.lagradost.cloudstream4.ui.components + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.size +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.click +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTouchInput +import androidx.compose.ui.test.runComposeUiTest +import androidx.compose.ui.unit.dp +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +/** + * Covers the two distinct interaction models of [Modifier.tvFocusable]: + * - Phone/touch (`isTV = false`): behaves like a normal clickable, one tap = one click. + * - TV (`isTV = true`): the first tap only requests focus, a second tap while + * focused is what actually invokes [onClick] (mirrors D-pad "select" behavior). + */ +@OptIn(ExperimentalTestApi::class) +class TvFocusableTest { + + @Test + fun nonTvModeInvokesOnClickOnASingleTap() = runComposeUiTest { + var clickCount = 0 + setContent { + Box( + modifier = Modifier + .size(48.dp) + .testTag("target") + .tvFocusable(isTV = false, onClick = { clickCount++ }), + ) + } + + onNodeWithTag("target").performClick() + waitForIdle() + + assertEquals(1, clickCount) + } + + @Test + fun nonTvModeInvokesOnClickOncePerTap() = runComposeUiTest { + var clickCount = 0 + setContent { + Box( + modifier = Modifier + .size(48.dp) + .testTag("target") + .tvFocusable(isTV = false, onClick = { clickCount++ }), + ) + } + + val target = onNodeWithTag("target") + target.performClick() + target.performClick() + target.performClick() + waitForIdle() + + assertEquals(3, clickCount) + } + + @Test + fun tvModeFirstTapOnlyRequestsFocusAndDoesNotInvokeOnClick() = runComposeUiTest { + var clickCount = 0 + setContent { + Box( + modifier = Modifier + .size(48.dp) + .testTag("target") + .tvFocusable(isTV = true, onClick = { clickCount++ }), + ) + } + + onNodeWithTag("target").performTouchInput { click() } + waitForIdle() + + assertEquals(0, clickCount) + } + + @Test + fun tvModeSecondTapWhileFocusedInvokesOnClick() = runComposeUiTest { + var clickCount = 0 + setContent { + Box( + modifier = Modifier + .size(48.dp) + .testTag("target") + .tvFocusable(isTV = true, onClick = { clickCount++ }), + ) + } + + val target = onNodeWithTag("target") + target.performTouchInput { click() } // 1st tap: gains focus + waitForIdle() + target.performTouchInput { click() } // 2nd tap: now focused, should click + waitForIdle() + + assertEquals(1, clickCount) + } + + @Test + fun tvModeReportsFocusChangesThroughCallback() = runComposeUiTest { + var focused = false + setContent { + Box( + modifier = Modifier + .size(48.dp) + .testTag("target") + .tvFocusable( + isTV = true, + onClick = {}, + onFocusChanged = { focused = it }, + ), + ) + } + + onNodeWithTag("target").performTouchInput { click() } + waitForIdle() + + assertTrue(focused, "Expected the element to report focus after the first tap") + } +} From 751ca276caa0a9e2cf9dcc63cc159ca04606e3fa Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:22:47 -0600 Subject: [PATCH 005/122] v2 --- .../com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt index 0a2a48c3203..b3630b5a1f8 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt @@ -9,7 +9,7 @@ import androidx.compose.ui.test.click import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTouchInput -import androidx.compose.ui.test.runComposeUiTest +import androidx.compose.ui.test.v2.runComposeUiTest import androidx.compose.ui.unit.dp import kotlin.test.Test import kotlin.test.assertEquals From 4e3bdf3b34cb778be169ee6a7017836c4351ecfe Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:26:26 -0600 Subject: [PATCH 006/122] Test --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b563ba46f87..c84ee794bb3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ annotation = "1.10.0" appcompat = "1.7.1" biometric = "1.4.0-alpha07" buildkonfigGradlePlugin = "0.21.2" -coil = { strictly = "3.3.0" } # Later versions require jvmTarget 11 or later +coil = { strictly = "3.5.0" } # Later versions require jvmTarget 11 or later colorpicker = "6b46b49" composeMaterial3 = "1.11.0-alpha07" composeMultiplatform = "1.11.1" From 04d7d42970624d72efc134ac24d11c56a5d311df Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:30:01 -0600 Subject: [PATCH 007/122] Test --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index c84ee794bb3..dc8aaebb377 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -58,7 +58,7 @@ video = "1.0.0" workRuntimeKtx = "2.11.2" zipline = "1.27.0" -jvmTarget = "1.8" +jvmTarget = "11" jdkToolchain = "17" minSdk = "23" compileSdk = "36" From 6cf05ae242ac30839e7738de97261a97ae267a3d Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:35:07 -0600 Subject: [PATCH 008/122] Fix --- composeApp/build.gradle.kts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 22f4d8c42dd..2b8ff68f1a2 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -52,6 +52,10 @@ kotlin { implementation(libs.compose.ui.test) implementation(libs.kotlin.test) } + + jvmTest.dependencies { + implementation(compose.desktop.currentOs) + } } } From eb0f05f08aea2e987da6c146ecdd18d248868da2 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:42:29 -0600 Subject: [PATCH 009/122] Add test --- .../ui/components/ProfilePictureTest.kt | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePictureTest.kt diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePictureTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePictureTest.kt new file mode 100644 index 00000000000..80b7ea4d6a7 --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePictureTest.kt @@ -0,0 +1,94 @@ +package com.lagradost.cloudstream4.ui.components + +import androidx.compose.foundation.layout.Column +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.assertCountEquals +import androidx.compose.ui.test.assertHeightIsEqualTo +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertWidthIsEqualTo +import androidx.compose.ui.test.onAllNodesWithContentDescription +import androidx.compose.ui.test.onNodeWithContentDescription +import androidx.compose.ui.test.v2.runComposeUiTest +import androidx.compose.ui.unit.dp +import com.lagradost.cloudstream4.ui.theme.CloudStreamTheme +import kotlin.test.Test + +/** + * The content description ("Profile picture") comes from + * `Res.string.profile_picture_desc`, see composeResources/values/strings.xml. + */ +@OptIn(ExperimentalTestApi::class) +class ProfilePictureTest { + + @Test + fun rendersLocalFallbackImageWhenNoUrlIsProvided() = runComposeUiTest { + setContent { + CloudStreamTheme { + ProfilePicture(profileImage = ProfileImage.BLUE) + } + } + waitForIdle() + + onNodeWithContentDescription("Profile picture").assertIsDisplayed() + } + + @Test + fun rendersRemoteImageWhenUrlIsProvided() = runComposeUiTest { + setContent { + CloudStreamTheme { + ProfilePicture( + profileImage = ProfileImage.TEAL, + profilePictureUrl = "https://example.com/avatar.png", + ) + } + } + waitForIdle() + + onNodeWithContentDescription("Profile picture").assertIsDisplayed() + } + + @Test + fun usesTheDefaultFiftyDpSizeWhenNotOverridden() = runComposeUiTest { + setContent { + CloudStreamTheme { + ProfilePicture(profileImage = ProfileImage.PURPLE) + } + } + waitForIdle() + + onNodeWithContentDescription("Profile picture") + .assertWidthIsEqualTo(50.dp) + .assertHeightIsEqualTo(50.dp) + } + + @Test + fun respectsACustomSize() = runComposeUiTest { + setContent { + CloudStreamTheme { + ProfilePicture(profileImage = ProfileImage.PINK, size = 96.dp) + } + } + waitForIdle() + + onNodeWithContentDescription("Profile picture") + .assertWidthIsEqualTo(96.dp) + .assertHeightIsEqualTo(96.dp) + } + + @Test + fun rendersForEveryProfileImageVariantWithoutCrashing() = runComposeUiTest { + setContent { + CloudStreamTheme { + Column { + for (variant in ProfileImage.entries) { + ProfilePicture(profileImage = variant) + } + } + } + } + waitForIdle() + + onAllNodesWithContentDescription("Profile picture") + .assertCountEquals(ProfileImage.entries.size) + } +} From 109810709bb6939240a259de15925a2398ec2226 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:43:57 -0600 Subject: [PATCH 010/122] Add test --- .../components/settings/SettingsItemTest.kt | 106 ++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt new file mode 100644 index 00000000000..84f5a2524ca --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt @@ -0,0 +1,106 @@ +package com.lagradost.cloudstream4.ui.components.settings + +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.assertCountEquals +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.onAllNodesWithText +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.runComposeUiTest +import com.lagradost.cloudstream4.ui.icons.tune +import com.lagradost.cloudstream4.ui.theme.CloudStreamTheme +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@OptIn(ExperimentalTestApi::class) +class SettingsItemTest { + + @Test + fun displaysTitleAndSubtitleWhenBothAreProvided() = runComposeUiTest { + setContent { + CloudStreamTheme { + SettingsItem( + title = "My Setting", + subtitle = "A helpful subtitle", + onClick = {}, + ) + } + } + waitForIdle() + + onNodeWithText("My Setting").assertIsDisplayed() + onNodeWithText("A helpful subtitle").assertIsDisplayed() + } + + @Test + fun omitsTheSubtitleNodeWhenSubtitleIsNull() = runComposeUiTest { + setContent { + CloudStreamTheme { + SettingsItem(title = "Only Title", subtitle = null, onClick = {}) + } + } + waitForIdle() + + onNodeWithText("Only Title").assertIsDisplayed() + onAllNodesWithText("A helpful subtitle").assertCountEquals(0) + } + + @Test + fun clickingAnywhereOnTheRowInvokesOnClick() = runComposeUiTest { + var clickCount = 0 + setContent { + CloudStreamTheme { + SettingsItem(title = "Clickable Setting", onClick = { clickCount++ }) + } + } + + onNodeWithText("Clickable Setting").performClick() + waitForIdle() + + assertEquals(1, clickCount) + } + + @Test + fun rendersSuccessfullyWithAnIconAttached() = runComposeUiTest { + setContent { + CloudStreamTheme { + SettingsItem(title = "With Icon", subtitle = "Has an icon", icon = tune, onClick = {}) + } + } + waitForIdle() + + onNodeWithText("With Icon").assertIsDisplayed() + onNodeWithText("Has an icon").assertIsDisplayed() + } + + @Test + fun rendersSuccessfullyWithoutAnIcon() = runComposeUiTest { + setContent { + CloudStreamTheme { + SettingsItem(title = "No Icon Here", icon = null, onClick = {}) + } + } + waitForIdle() + + onNodeWithText("No Icon Here").assertIsDisplayed() + } + + @Test + fun multipleClicksEachInvokeOnClick() = runComposeUiTest { + var clickCount = 0 + setContent { + CloudStreamTheme { + SettingsItem(title = "Repeatable", onClick = { clickCount++ }) + } + } + + val node = onNodeWithText("Repeatable") + node.performClick() + node.performClick() + node.performClick() + waitForIdle() + + assertTrue(clickCount == 3, "Expected 3 clicks but got $clickCount") + } +} From aac8f7303c2c4e20a6d4100657c8bda079a6f537 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:45:45 -0600 Subject: [PATCH 011/122] Fix --- .../cloudstream4/ui/components/settings/SettingsItemTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt index 84f5a2524ca..09ece513a2d 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt @@ -6,7 +6,7 @@ import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick -import androidx.compose.ui.test.runComposeUiTest +import androidx.compose.ui.test.v2.runComposeUiTest import com.lagradost.cloudstream4.ui.icons.tune import com.lagradost.cloudstream4.ui.theme.CloudStreamTheme import kotlin.test.Test From 3c923a2a1230609b74052ee08a84d3d0f8fc973c Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:47:20 -0600 Subject: [PATCH 012/122] Add test --- .../ui/theme/CloudStreamThemeTest.kt | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeTest.kt diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeTest.kt new file mode 100644 index 00000000000..14c9e2b3516 --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeTest.kt @@ -0,0 +1,119 @@ +package com.lagradost.cloudstream4.ui.theme + +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.v2.runComposeUiTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertTrue + +/** + * Verifies that [CloudStreamTheme] resolves the right [CloudStreamColorScheme] + * for each [CloudStreamThemeMode], and that explicit primary color overrides apply. + */ +@OptIn(ExperimentalTestApi::class) +class CloudStreamThemeTest { + + @Test + fun darkModeExposesADarkColorScheme() = runComposeUiTest { + lateinit var colors: CloudStreamColorScheme + setContent { + CloudStreamTheme(mode = CloudStreamThemeMode.Dark) { + colors = CloudStreamTheme.colors + } + } + waitForIdle() + + assertFalse(colors.isLight) + assertEquals(CloudStreamPalette.DarkBlackBg, colors.background) + } + + @Test + fun lightModeExposesALightColorScheme() = runComposeUiTest { + lateinit var colors: CloudStreamColorScheme + setContent { + CloudStreamTheme(mode = CloudStreamThemeMode.Light) { + colors = CloudStreamTheme.colors + } + } + waitForIdle() + + assertTrue(colors.isLight) + assertEquals(CloudStreamPalette.LightBlackBg, colors.background) + } + + @Test + fun amoledModeUsesPureBlackForEveryBackgroundLayer() = runComposeUiTest { + lateinit var colors: CloudStreamColorScheme + setContent { + CloudStreamTheme(mode = CloudStreamThemeMode.Amoled) { + colors = CloudStreamTheme.colors + } + } + waitForIdle() + + assertEquals(CloudStreamPalette.AmoledBlack, colors.background) + assertEquals(CloudStreamPalette.AmoledBlack, colors.surface) + assertEquals(CloudStreamPalette.AmoledBlack, colors.surfaceVariant) + assertEquals(CloudStreamPalette.AmoledBlack, colors.surfaceContainer) + } + + @Test + fun draculaModeExposesTheDraculaScheme() = runComposeUiTest { + lateinit var colors: CloudStreamColorScheme + setContent { + CloudStreamTheme(mode = CloudStreamThemeMode.Dracula) { + colors = CloudStreamTheme.colors + } + } + waitForIdle() + + assertEquals(CloudStreamPalette.DraculaBlackBg, colors.background) + assertFalse(colors.isLight) + } + + @Test + fun explicitPrimaryColorOverridesTheSchemeDefaultPrimary() = runComposeUiTest { + lateinit var colors: CloudStreamColorScheme + setContent { + CloudStreamTheme( + mode = CloudStreamThemeMode.Dark, + primaryColor = CloudStreamPrimaryColor.RED, + ) { + colors = CloudStreamTheme.colors + } + } + waitForIdle() + + assertEquals(CloudStreamPrimaryColor.RED.color, colors.primary) + } + + @Test + fun defaultPrimaryColorMatchesNormal() = runComposeUiTest { + lateinit var colors: CloudStreamColorScheme + setContent { + CloudStreamTheme(mode = CloudStreamThemeMode.Dark) { + colors = CloudStreamTheme.colors + } + } + waitForIdle() + + assertEquals(CloudStreamPrimaryColor.NORMAL.color, colors.primary) + } + + @Test + fun colorsAreProvidedThroughLocalCloudStreamColors() = runComposeUiTest { + lateinit var fromLocal: CloudStreamColorScheme + lateinit var fromObject: CloudStreamColorScheme + setContent { + CloudStreamTheme(mode = CloudStreamThemeMode.SilentBlue) { + fromLocal = LocalCloudStreamColors.current + fromObject = CloudStreamTheme.colors + } + } + waitForIdle() + + assertEquals(fromLocal.background, fromObject.background) + assertEquals(CloudStreamPalette.SilentBlueBlackBg, fromLocal.background) + } +} From 5503fc4bb592f87303266447b4686e9eaab16529 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:52:38 -0600 Subject: [PATCH 013/122] Fix --- .../ui/components/settings/SettingsItemTest.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt index 09ece513a2d..115e95d8308 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt @@ -9,6 +9,8 @@ import androidx.compose.ui.test.performClick import androidx.compose.ui.test.v2.runComposeUiTest import com.lagradost.cloudstream4.ui.icons.tune import com.lagradost.cloudstream4.ui.theme.CloudStreamTheme +import com.lagradost.cloudstream4.utils.DeviceLayout +import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue @@ -16,6 +18,11 @@ import kotlin.test.assertTrue @OptIn(ExperimentalTestApi::class) class SettingsItemTest { + @BeforeTest + fun resolveDeviceLayout() { + DeviceLayout.update() + } + @Test fun displaysTitleAndSubtitleWhenBothAreProvided() = runComposeUiTest { setContent { From 5963dbba92c75324a7403074cda58bd09faa2f0b Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 16:56:46 -0600 Subject: [PATCH 014/122] Add test --- .../ui/screens/settings/SettingsScreenTest.kt | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt new file mode 100644 index 00000000000..384594bf2ae --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt @@ -0,0 +1,169 @@ +package com.lagradost.cloudstream4.ui.screens.settings + +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.longClick +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.onParent +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTouchInput +import androidx.compose.ui.test.v2.runComposeUiTest +import com.lagradost.cloudstream4.ui.components.ProfileImage +import com.lagradost.cloudstream4.ui.theme.CloudStreamTheme +import com.lagradost.cloudstream4.utils.DeviceLayout +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +@OptIn(ExperimentalTestApi::class) +class SettingsScreenTest { + + @BeforeTest + fun resolveDeviceLayout() { + DeviceLayout.update() + } + + private val profile = SettingsProfileState(name = "Test User", profileImage = ProfileImage.DARK_BLUE) + private val version = SettingsVersionState( + appVersion = "1.0.0-PRE", + commitHash = "abc1234", + buildDate = "Jan 1, 2026", + ) + + @Test + fun displaysTheProfileName() = runComposeUiTest { + setContent { + CloudStreamTheme { + SettingsScreen(profile = profile, version = version, onNavigate = {}) + } + } + waitForIdle() + + onNodeWithText("Test User").assertIsDisplayed() + } + + @Test + fun displaysEveryCategoryTitleAndSubtitle() = runComposeUiTest { + // Resolve the localized strings from within the same composition so this + // test stays correct even if the copy in strings.xml changes. + lateinit var expected: List> + + setContent { + expected = SettingsCategory.entries.map { it.label() to it.subtitle() } + CloudStreamTheme { + SettingsScreen(profile = profile, version = version, onNavigate = {}) + } + } + waitForIdle() + + expected.forEach { (title, subtitle) -> + onNodeWithText(title).assertIsDisplayed() + onNodeWithText(subtitle).assertIsDisplayed() + } + } + + @Test + fun clickingACategoryInvokesOnNavigateWithThatCategory() = runComposeUiTest { + val navigated = mutableListOf() + lateinit var playerTitle: String + + setContent { + playerTitle = SettingsCategory.PLAYER.label() + CloudStreamTheme { + SettingsScreen( + profile = profile, + version = version, + onNavigate = { navigated += it }, + ) + } + } + waitForIdle() + + onNodeWithText(playerTitle).performClick() + waitForIdle() + + assertEquals(listOf(SettingsCategory.PLAYER), navigated) + } + + @Test + fun clickingEachCategoryNavigatesExactlyOnce() = runComposeUiTest { + val navigated = mutableListOf() + lateinit var titlesInOrder: List> + + setContent { + titlesInOrder = SettingsCategory.entries.map { it to it.label() } + CloudStreamTheme { + SettingsScreen( + profile = profile, + version = version, + onNavigate = { navigated += it }, + ) + } + } + waitForIdle() + + titlesInOrder.forEach { (_, title) -> onNodeWithText(title).performClick() } + waitForIdle() + + assertEquals(titlesInOrder.map { it.first }, navigated) + } + + @Test + fun displaysAllThreeVersionChips() = runComposeUiTest { + setContent { + CloudStreamTheme { + SettingsScreen(profile = profile, version = version, onNavigate = {}) + } + } + waitForIdle() + + onNodeWithText(version.appVersion).assertIsDisplayed() + onNodeWithText(version.commitHash).assertIsDisplayed() + onNodeWithText(version.buildDate).assertIsDisplayed() + } + + @Test + fun longPressingTheVersionFooterInvokesOnVersionLongClick() = runComposeUiTest { + var longClicked = false + setContent { + CloudStreamTheme { + SettingsScreen( + profile = profile, + version = version, + onNavigate = {}, + onVersionLongClick = { longClicked = true }, + ) + } + } + waitForIdle() + + // The version chips have no click behavior of their own; the combinedClickable + // that handles the long-press lives on their shared parent Row. + onNodeWithText(version.appVersion).onParent().performTouchInput { longClick() } + waitForIdle() + + assertTrue(longClicked, "Expected long-pressing the version footer to fire onVersionLongClick") + } + + @Test + fun shortPressingTheVersionFooterDoesNotInvokeOnVersionLongClick() = runComposeUiTest { + var longClicked = false + setContent { + CloudStreamTheme { + SettingsScreen( + profile = profile, + version = version, + onNavigate = {}, + onVersionLongClick = { longClicked = true }, + ) + } + } + waitForIdle() + + onNodeWithText(version.appVersion).onParent().performClick() + waitForIdle() + + assertTrue(!longClicked) + } +} From fd79f9a911b2df5c0d329b74e7cd3896608966da Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:01:47 -0600 Subject: [PATCH 015/122] Fix? --- .../cloudstream4/ui/screens/settings/SettingsScreenTest.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt index 384594bf2ae..c7231e856b1 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt @@ -4,7 +4,6 @@ import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.longClick import androidx.compose.ui.test.onNodeWithText -import androidx.compose.ui.test.onParent import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.test.v2.runComposeUiTest @@ -138,9 +137,7 @@ class SettingsScreenTest { } waitForIdle() - // The version chips have no click behavior of their own; the combinedClickable - // that handles the long-press lives on their shared parent Row. - onNodeWithText(version.appVersion).onParent().performTouchInput { longClick() } + onNodeWithText(version.appVersion).performTouchInput { longClick() } waitForIdle() assertTrue(longClicked, "Expected long-pressing the version footer to fire onVersionLongClick") @@ -161,7 +158,7 @@ class SettingsScreenTest { } waitForIdle() - onNodeWithText(version.appVersion).onParent().performClick() + onNodeWithText(version.appVersion).performClick() waitForIdle() assertTrue(!longClicked) From 65606089f3b00e40f0b74390cbc386febd6d1ecd Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:04:04 -0600 Subject: [PATCH 016/122] - --- .../preferences/PreferenceDefaultsTest.kt | 31 ------------------- 1 file changed, 31 deletions(-) delete mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceDefaultsTest.kt diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceDefaultsTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceDefaultsTest.kt deleted file mode 100644 index dc71d5ecacd..00000000000 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceDefaultsTest.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.lagradost.cloudstream4.preferences - -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertNotEquals - -class PreferenceDefaultsTest { - - @Test - fun appThemeDefaultsToAmoledLight() { - assertEquals("AmoledLight", PreferenceDefaults.APP_THEME) - } - - @Test - fun primaryColorDefaultsToNormal() { - assertEquals("Normal", PreferenceDefaults.PRIMARY_COLOR) - } - - @Test - fun appLayoutDefaultsToAuto() { - // -1 is "Auto" - assertEquals(-1, PreferenceDefaults.APP_LAYOUT) - } - - @Test - fun appLayoutDoesNotCollideWithAnyExplicitLayoutValue() { - // Explicit layout values are 0 (Phone), 1 (TV), 2 (Emulator). - val explicitLayoutValues = setOf(0, 1, 2) - assertNotEquals(true, explicitLayoutValues.contains(PreferenceDefaults.APP_LAYOUT)) - } -} From 0b15fef307b2bd64f2bd86053552964fdc7628d2 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:04:47 -0600 Subject: [PATCH 017/122] - --- .../preferences/PreferenceKeysTest.kt | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt index c97f6774353..5a04ca3f1e3 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt @@ -1,26 +1,10 @@ package com.lagradost.cloudstream4.preferences import kotlin.test.Test -import kotlin.test.assertEquals import kotlin.test.assertTrue class PreferenceKeysTest { - @Test - fun appLayoutKeyHasExpectedValue() { - assertEquals("app_layout_key", PreferenceKeys.APP_LAYOUT) - } - - @Test - fun appThemeKeyHasExpectedValue() { - assertEquals("app_theme_key", PreferenceKeys.APP_THEME) - } - - @Test - fun primaryColorKeyHasExpectedValue() { - assertEquals("primary_color_key", PreferenceKeys.PRIMARY_COLOR) - } - @Test fun allPreferenceKeysAreUnique() { val keys = listOf( From 35bebe3eeb3accc35fac1d83e03897280cba39c3 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:07:02 -0600 Subject: [PATCH 018/122] Update --- .../com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt index b5d9e6a4eb5..bc4d570ef35 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt @@ -62,7 +62,8 @@ class DeviceLayoutTest { fun isLandscapeDoesNotThrow() { DeviceLayout.update() // Just verifying this completes without throwing on every target, - // since the actual boolean result is platform/environment dependent. + // since the actual result is platform/environment dependent, + // which we might add tests for eventually but not for now. DeviceLayout.isLandscape() } } From 0f0071961f7d4256d607f4d9a6cf655316aca685 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:12:26 -0600 Subject: [PATCH 019/122] - --- gradle/libs.versions.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dc8aaebb377..b563ba46f87 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -8,7 +8,7 @@ annotation = "1.10.0" appcompat = "1.7.1" biometric = "1.4.0-alpha07" buildkonfigGradlePlugin = "0.21.2" -coil = { strictly = "3.5.0" } # Later versions require jvmTarget 11 or later +coil = { strictly = "3.3.0" } # Later versions require jvmTarget 11 or later colorpicker = "6b46b49" composeMaterial3 = "1.11.0-alpha07" composeMultiplatform = "1.11.1" @@ -58,7 +58,7 @@ video = "1.0.0" workRuntimeKtx = "2.11.2" zipline = "1.27.0" -jvmTarget = "11" +jvmTarget = "1.8" jdkToolchain = "17" minSdk = "23" compileSdk = "36" From a575d65b1cab3ce3edd55710db4afc41ad4e03a7 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:17:25 -0600 Subject: [PATCH 020/122] Update --- .../cloudstream4/ui/screens/settings/SettingsScreenTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt index c7231e856b1..9498c288bd5 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreenTest.kt @@ -27,7 +27,7 @@ class SettingsScreenTest { private val version = SettingsVersionState( appVersion = "1.0.0-PRE", commitHash = "abc1234", - buildDate = "Jan 1, 2026", + buildDate = "Jan 1, 2026 12:00:00 AM", ) @Test From cf72155aa375b81e8e027603f9efcee5f0127253 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:18:17 -0600 Subject: [PATCH 021/122] Update --- .../cloudstream4/ui/screens/settings/SettingsScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt index 0f962bb6769..eabdd9f49a6 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt @@ -241,7 +241,7 @@ private fun SettingsScreenPreview() { version = SettingsVersionState( appVersion = "1.0.0-PRE", commitHash = "abc1234", - buildDate = "May 25, 2026 12:00:00 AM", + buildDate = "Jan 1, 2026 12:00:00 AM", ), onNavigate = {}, onVersionLongClick = {}, From 69bfcf31de1a2d4713eb88fb8e01897dbf90b428 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Wed, 1 Jul 2026 17:26:35 -0600 Subject: [PATCH 022/122] Try new test --- .../ui/components/CloudStreamRippleTest.kt | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) create mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/CloudStreamRippleTest.kt diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/CloudStreamRippleTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/CloudStreamRippleTest.kt new file mode 100644 index 00000000000..0e15f283839 --- /dev/null +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/CloudStreamRippleTest.kt @@ -0,0 +1,139 @@ +package com.lagradost.cloudstream4.ui.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.PressInteraction +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.size +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.toPixelMap +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.test.ExperimentalTestApi +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.captureToImage +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.v2.runComposeUiTest +import androidx.compose.ui.unit.dp +import com.lagradost.cloudstream4.ui.theme.CloudStreamPalette +import com.lagradost.cloudstream4.ui.theme.CloudStreamTheme +import com.lagradost.cloudstream4.ui.theme.CloudStreamThemeMode +import kotlin.test.Test +import kotlin.test.assertNotEquals + +@OptIn(ExperimentalTestApi::class) +class CloudStreamRippleTest { + + @Test + fun appliesSuccessfullyWhenBounded() = runComposeUiTest { + val interactionSource = MutableInteractionSource() + setContent { + CloudStreamTheme { + Box( + modifier = Modifier + .testTag("target") + .size(64.dp) + .cloudStreamRipple(interactionSource, bounded = true), + ) + } + } + waitForIdle() + + onNodeWithTag("target").assertIsDisplayed() + } + + @Test + fun appliesSuccessfullyWhenUnbounded() = runComposeUiTest { + val interactionSource = MutableInteractionSource() + setContent { + CloudStreamTheme { + Box( + modifier = Modifier + .testTag("target") + .size(64.dp) + .cloudStreamRipple(interactionSource, bounded = false), + ) + } + } + waitForIdle() + + onNodeWithTag("target").assertIsDisplayed() + } + + @Test + fun appliesSuccessfullyUnderEveryThemeMode() = runComposeUiTest { + val modes = CloudStreamThemeMode.entries.filterNot { it == CloudStreamThemeMode.Dynamic } + + setContent { + Column { + modes.forEach { mode -> + val interactionSource = remember { MutableInteractionSource() } + CloudStreamTheme(mode = mode) { + Box( + modifier = Modifier + .testTag("target-${mode.name}") + .size(32.dp) + .cloudStreamRipple(interactionSource), + ) + } + } + } + } + waitForIdle() + + modes.forEach { mode -> + onNodeWithTag("target-${mode.name}").assertIsDisplayed() + } + } + + /** + * Check rhat it emits a real [PressInteraction.Press] into the + * [MutableInteractionSource] and confirms the pixel under the press point + * actually changes once the ripple draws over it. + * + * This test may end up being flaky. If so, it can be dropped. + */ + @Test + fun pressInteractionChangesThePixelsUnderThePress() = runComposeUiTest { + val interactionSource = MutableInteractionSource() + val boxSizeDp = 80 + + setContent { + CloudStreamTheme(mode = CloudStreamThemeMode.Dark) { + Box( + modifier = Modifier + .testTag("target") + .size(boxSizeDp.dp) + .background(CloudStreamPalette.DarkBlackBg) + .cloudStreamRipple(interactionSource), + ) + } + } + waitForIdle() + + val target = onNodeWithTag("target") + val idlePixels = target.captureToImage().toPixelMap() + val centerX = idlePixels.width / 2 + val centerY = idlePixels.height / 2 + val idleCenterColor = idlePixels[centerX, centerY] + + val press = PressInteraction.Press(Offset(centerX.toFloat(), centerY.toFloat())) + interactionSource.tryEmit(press) + waitForIdle() + + val pressedPixels = target.captureToImage().toPixelMap() + val pressedCenterColor = pressedPixels[centerX, centerY] + + assertNotEquals( + idleCenterColor, + pressedCenterColor, + "Expected the ripple to visibly change the pixel under the press point", + ) + + // Release cleanly so the ripple doesn't leak a running animation past the test. + interactionSource.tryEmit(PressInteraction.Release(press)) + waitForIdle() + } +} From e6497928ddb1dbce5fcea11088cecdf3244fc0b6 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 4 Jul 2026 20:37:14 -0600 Subject: [PATCH 023/122] Update --- .../cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt index 4139223ffdc..0f77213c5a8 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt @@ -61,7 +61,7 @@ class CloudStreamColorSchemeTest { @Test fun allNamedSchemesSharePrimaryAndOngoingColors() { // Every preset (aside from copies that explicitly override it) shares the - // same brand primary/ongoing accent colors from the palette. + // same default primary/ongoing accent colors from the palette. val schemes = listOf( darkScheme(), lightScheme(), draculaScheme(), lavenderScheme(), silentBlueScheme(), ) From 05a01c89297a1fb1b8d199cadfcc5091688cbd4c Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 4 Jul 2026 20:56:32 -0600 Subject: [PATCH 024/122] Add to jvmTest --- .../preferences/PreferenceKeysTest.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt diff --git a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt new file mode 100644 index 00000000000..70598b12d8e --- /dev/null +++ b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt @@ -0,0 +1,18 @@ +package com.lagradost.cloudstream4.preferences + +import kotlin.reflect.full.memberProperties +import kotlin.test.Test +import kotlin.test.assertTrue + +class PreferenceKeysTest { + + @Test + fun allPreferenceKeysAreUnique() { + val keys = PreferenceKeys::class.memberProperties + .filter { it.returnType.classifier == String::class } + .map { it.getter.call(PreferenceKeys) as String } + + assertTrue(keys.isNotEmpty(), "No preference keys found via reflection") + assertTrue(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") + } +} From 5d2ba24461cf0e4e53631ca911562ef3787be998 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 4 Jul 2026 20:57:59 -0600 Subject: [PATCH 025/122] Add --- composeApp/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 2b8ff68f1a2..73d657376a1 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -55,6 +55,8 @@ kotlin { jvmTest.dependencies { implementation(compose.desktop.currentOs) + implementation(libs.kotlin.reflect) + implementation(libs.kotlin.test) } } } From 3f63b0674a8c3ef9fcf1edb351a8c3f9176929ff Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 4 Jul 2026 20:58:40 -0600 Subject: [PATCH 026/122] - --- .../preferences/PreferenceKeysTest.kt | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt deleted file mode 100644 index 5a04ca3f1e3..00000000000 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt +++ /dev/null @@ -1,17 +0,0 @@ -package com.lagradost.cloudstream4.preferences - -import kotlin.test.Test -import kotlin.test.assertTrue - -class PreferenceKeysTest { - - @Test - fun allPreferenceKeysAreUnique() { - val keys = listOf( - PreferenceKeys.APP_LAYOUT, - PreferenceKeys.APP_THEME, - PreferenceKeys.PRIMARY_COLOR, - ) - assertTrue(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") - } -} From 5ca4c14e8456109c33d8e80e040d019ff7e22e1c Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 4 Jul 2026 23:11:31 -0600 Subject: [PATCH 027/122] Try fix --- .../cloudstream4/preferences/PreferenceKeysTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt index 70598b12d8e..6b01ac23091 100644 --- a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt +++ b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt @@ -1,6 +1,6 @@ package com.lagradost.cloudstream4.preferences -import kotlin.reflect.full.memberProperties +import kotlin.reflect.full.declaredMemberProperties import kotlin.test.Test import kotlin.test.assertTrue @@ -8,9 +8,9 @@ class PreferenceKeysTest { @Test fun allPreferenceKeysAreUnique() { - val keys = PreferenceKeys::class.memberProperties + val keys = PreferenceKeys::class.declaredMemberProperties .filter { it.returnType.classifier == String::class } - .map { it.getter.call(PreferenceKeys) as String } + .map { prop -> prop.call(PreferenceKeys) as String } assertTrue(keys.isNotEmpty(), "No preference keys found via reflection") assertTrue(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") From 0959f8efc7135d4e4bb2cacf42c2ed07c0dec18b Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 4 Jul 2026 23:17:34 -0600 Subject: [PATCH 028/122] Try a different way --- .../cloudstream4/preferences/PreferenceKeysTest.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt index 6b01ac23091..a8a901b6be6 100644 --- a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt +++ b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt @@ -1,6 +1,6 @@ package com.lagradost.cloudstream4.preferences -import kotlin.reflect.full.declaredMemberProperties +import java.io.File import kotlin.test.Test import kotlin.test.assertTrue @@ -8,11 +8,11 @@ class PreferenceKeysTest { @Test fun allPreferenceKeysAreUnique() { - val keys = PreferenceKeys::class.declaredMemberProperties - .filter { it.returnType.classifier == String::class } - .map { prop -> prop.call(PreferenceKeys) as String } + val sourceFile = File("src/commonMain/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeys.kt") + val regex = """const val \w+ = "([^"]+)"""".toRegex() + val keys = regex.findAll(sourceFile.readText()).map { it.groupValues[1] }.toList() - assertTrue(keys.isNotEmpty(), "No preference keys found via reflection") + assertTrue(keys.isNotEmpty(), "No preference keys found in source") assertTrue(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") } } From 10d09d56c510f45cd6349a099a12f1826e66abe8 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 4 Jul 2026 23:19:57 -0600 Subject: [PATCH 029/122] Test --- .../lagradost/cloudstream4/preferences/PreferenceKeysTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt index a8a901b6be6..8522faac8de 100644 --- a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt +++ b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt @@ -2,6 +2,7 @@ package com.lagradost.cloudstream4.preferences import java.io.File import kotlin.test.Test +import kotlin.test.assertFalse import kotlin.test.assertTrue class PreferenceKeysTest { @@ -13,6 +14,6 @@ class PreferenceKeysTest { val keys = regex.findAll(sourceFile.readText()).map { it.groupValues[1] }.toList() assertTrue(keys.isNotEmpty(), "No preference keys found in source") - assertTrue(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") + assertFalse(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") } } From 0fc1748b7f665dc376c36dba7a1ccae3f651f5b9 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:07:30 -0600 Subject: [PATCH 030/122] Try --- .github/workflows/pull_request.yml | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 8f5c6286636..6b190c90610 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -29,8 +29,28 @@ jobs: - name: Run Gradle run: ./gradlew assemblePrereleaseDebug lint check - - name: Upload Artifact + - name: Upload APK + if: always() uses: actions/upload-artifact@v7 with: name: pull-request-build path: "app/build/outputs/apk/prerelease/debug/*.apk" + + - name: Upload Test Results + if: always() + uses: actions/upload-artifact@v7 + with: + name: test-results + path: | + **/build/reports/tests/ + **/build/test-results/ + include-hidden-files: false + + - name: Upload Lint Results + if: always() + uses: actions/upload-artifact@v7 + with: + name: lint-results + path: | + **/build/reports/lint-results-*.html + **/build/reports/lint-results-*.xml From 84275b74e6e5be39cf37d98d7846a9300fab965f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:16:19 -0600 Subject: [PATCH 031/122] Try reflect --- .../cloudstream4/preferences/PreferenceKeysTest.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt index 8522faac8de..84abbbe0e73 100644 --- a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt +++ b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt @@ -1,19 +1,18 @@ package com.lagradost.cloudstream4.preferences -import java.io.File +import kotlin.reflect.full.declaredMemberProperties import kotlin.test.Test -import kotlin.test.assertFalse import kotlin.test.assertTrue class PreferenceKeysTest { @Test fun allPreferenceKeysAreUnique() { - val sourceFile = File("src/commonMain/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeys.kt") - val regex = """const val \w+ = "([^"]+)"""".toRegex() - val keys = regex.findAll(sourceFile.readText()).map { it.groupValues[1] }.toList() + val keys = PreferenceKeys::class.declaredMemberProperties + .filter { it.returnType.classifier == String::class } + .map { it.get(PreferenceKeys) } - assertTrue(keys.isNotEmpty(), "No preference keys found in source") - assertFalse(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") + assertTrue(keys.isNotEmpty(), "No preference keys found via reflection") + assertTrue(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") } } From 71b6c9ca19d6be3af677faf1b2c90e60cc36ba1d Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:22:34 -0600 Subject: [PATCH 032/122] Try fix --- .../cloudstream4/preferences/PreferenceKeysTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt index 84abbbe0e73..4532b9d87c5 100644 --- a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt +++ b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt @@ -1,6 +1,6 @@ package com.lagradost.cloudstream4.preferences -import kotlin.reflect.full.declaredMemberProperties +import kotlin.reflect.full.staticProperties import kotlin.test.Test import kotlin.test.assertTrue @@ -8,9 +8,9 @@ class PreferenceKeysTest { @Test fun allPreferenceKeysAreUnique() { - val keys = PreferenceKeys::class.declaredMemberProperties + val keys = PreferenceKeys::class.staticProperties .filter { it.returnType.classifier == String::class } - .map { it.get(PreferenceKeys) } + .map { it.get() } assertTrue(keys.isNotEmpty(), "No preference keys found via reflection") assertTrue(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") From 93417eb2e31342f621c7f479e1dcdfe81d215b28 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:30:19 -0600 Subject: [PATCH 033/122] Try fix --- .../cloudstream4/preferences/PreferenceKeysTest.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt index 4532b9d87c5..5ee526e9835 100644 --- a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt +++ b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt @@ -1,6 +1,7 @@ package com.lagradost.cloudstream4.preferences -import kotlin.reflect.full.staticProperties +import kotlin.reflect.KProperty1 +import kotlin.reflect.full.declaredMemberProperties import kotlin.test.Test import kotlin.test.assertTrue @@ -8,9 +9,12 @@ class PreferenceKeysTest { @Test fun allPreferenceKeysAreUnique() { - val keys = PreferenceKeys::class.staticProperties + val keys = PreferenceKeys::class.declaredMemberProperties .filter { it.returnType.classifier == String::class } - .map { it.get() } + .map { + @Suppress("UNCHECKED_CAST") + (it as KProperty1).get(PreferenceKeys) + } assertTrue(keys.isNotEmpty(), "No preference keys found via reflection") assertTrue(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") From ea2c295e28a641f5c0c7a738e5c69fcce4a5a607 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:32:25 -0600 Subject: [PATCH 034/122] Try --- .../cloudstream4/preferences/PreferenceKeysTest.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt index 5ee526e9835..fbfbadcfd49 100644 --- a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt +++ b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt @@ -1,6 +1,5 @@ package com.lagradost.cloudstream4.preferences -import kotlin.reflect.KProperty1 import kotlin.reflect.full.declaredMemberProperties import kotlin.test.Test import kotlin.test.assertTrue @@ -11,10 +10,7 @@ class PreferenceKeysTest { fun allPreferenceKeysAreUnique() { val keys = PreferenceKeys::class.declaredMemberProperties .filter { it.returnType.classifier == String::class } - .map { - @Suppress("UNCHECKED_CAST") - (it as KProperty1).get(PreferenceKeys) - } + .map { it.getter.call() } assertTrue(keys.isNotEmpty(), "No preference keys found via reflection") assertTrue(keys.toSet().size == keys.size, "Preference keys must not collide: $keys") From de2906e97073ae404494ab96075a8f1da8cea943 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 14:29:43 -0600 Subject: [PATCH 035/122] Add comment --- .../cloudstream4/preferences/PreferenceKeysTest.kt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt index fbfbadcfd49..f958823447c 100644 --- a/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt +++ b/composeApp/src/jvmTest/kotlin/com/lagradost/cloudstream4/preferences/PreferenceKeysTest.kt @@ -4,6 +4,13 @@ import kotlin.reflect.full.declaredMemberProperties import kotlin.test.Test import kotlin.test.assertTrue +/** + * This is in jvmTest rather than commonTest so that we can + * use kotlin-reflect (JVM-only) rather than having to + * manually maintain two seperate lists for these. + * Preferences keys should be unique, so this is + * just to ensure that. + */ class PreferenceKeysTest { @Test From d310fa3171b3ad62062747a2e0a61e5b53555fea Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 14:30:58 -0600 Subject: [PATCH 036/122] - --- .github/workflows/pull_request.yml | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 6b190c90610..8f5c6286636 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -29,28 +29,8 @@ jobs: - name: Run Gradle run: ./gradlew assemblePrereleaseDebug lint check - - name: Upload APK - if: always() + - name: Upload Artifact uses: actions/upload-artifact@v7 with: name: pull-request-build path: "app/build/outputs/apk/prerelease/debug/*.apk" - - - name: Upload Test Results - if: always() - uses: actions/upload-artifact@v7 - with: - name: test-results - path: | - **/build/reports/tests/ - **/build/test-results/ - include-hidden-files: false - - - name: Upload Lint Results - if: always() - uses: actions/upload-artifact@v7 - with: - name: lint-results - path: | - **/build/reports/lint-results-*.html - **/build/reports/lint-results-*.xml From eca7b7a499b457f83393b73e4bb326c07b6b2ed3 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 14:33:18 -0600 Subject: [PATCH 037/122] + --- .github/workflows/pull_request.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 8f5c6286636..506090f9a32 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -34,3 +34,22 @@ jobs: with: name: pull-request-build path: "app/build/outputs/apk/prerelease/debug/*.apk" + + - name: Upload Test Results + if: always() + uses: actions/upload-artifact@v7 + with: + name: test-results + path: | + **/build/reports/tests/ + **/build/test-results/ + include-hidden-files: false + + - name: Upload Lint Results + if: always() + uses: actions/upload-artifact@v7 + with: + name: lint-results + path: | + **/build/reports/lint-results-*.html + **/build/reports/lint-results-*.xml From 30d55a4164ba2e17caff0f59d1344a2b7fd83595 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 14:36:47 -0600 Subject: [PATCH 038/122] Use LocalResources --- .../cloudstream4/ui/theme/DynamicPrimaryColor.android.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt index e7f7fbed958..e309b857927 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt @@ -3,11 +3,11 @@ package com.lagradost.cloudstream4.ui.theme import android.os.Build import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalResources @Composable actual fun resolveDynamicPrimaryColor(): Color { - val resources = LocalContext.current.resources + val resources = LocalResources.current return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) Color(resources.getColor(android.R.color.system_accent1_200, null)) else CloudStreamPrimaryColor.NORMAL.color @@ -15,7 +15,7 @@ actual fun resolveDynamicPrimaryColor(): Color { @Composable actual fun resolveDynamicSecondaryColor(): Color { - val resources = LocalContext.current.resources + val resources = LocalResources.current return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) Color(resources.getColor(android.R.color.system_accent2_200, null)) else CloudStreamPrimaryColor.NORMAL.color From 93d8fee42ff71c70b2836326c7d947315115627f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 14:40:09 -0600 Subject: [PATCH 039/122] fix --- .../cloudstream4/ui/theme/DynamicPrimaryColor.android.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt index e309b857927..e46218fd459 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt @@ -3,20 +3,18 @@ package com.lagradost.cloudstream4.ui.theme import android.os.Build import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalResources +import androidx.compose.ui.res.colorResource @Composable actual fun resolveDynamicPrimaryColor(): Color { - val resources = LocalResources.current return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) - Color(resources.getColor(android.R.color.system_accent1_200, null)) + colorResource(android.R.color.system_accent1_200) else CloudStreamPrimaryColor.NORMAL.color } @Composable actual fun resolveDynamicSecondaryColor(): Color { - val resources = LocalResources.current return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) - Color(resources.getColor(android.R.color.system_accent2_200, null)) + colorResource(android.R.color.system_accent2_200) else CloudStreamPrimaryColor.NORMAL.color } From 1f30a58e1a45854002824747c643a747513066c0 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 14:51:24 -0600 Subject: [PATCH 040/122] Simplify --- .../ui/theme/DynamicTheme.android.kt | 50 ++++++++----------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt index 95116d18681..0f6574924b3 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt @@ -1,49 +1,43 @@ package com.lagradost.cloudstream4.ui.theme -import android.content.Context -import android.content.res.Configuration import android.os.Build -import androidx.annotation.RequiresApi +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.colorResource @Composable actual fun resolveDynamicTheme(): CloudStreamColorScheme { - val context = LocalContext.current return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) - context.buildMonetScheme() + buildMonetScheme() else darkScheme() } -@RequiresApi(Build.VERSION_CODES.S) -fun Context.buildMonetScheme(): CloudStreamColorScheme { - val isSystemDark = (resources.configuration.uiMode and - Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES - +@Composable +private fun buildMonetScheme(): CloudStreamColorScheme { + val isSystemDark = isSystemInDarkTheme() return if (isSystemDark) { CloudStreamColorScheme( - background = Color(getColor(android.R.color.system_neutral1_900)), - surfaceVariant = Color(getColor(android.R.color.system_neutral1_800)), - surface = Color(getColor(android.R.color.system_neutral1_800)), - surfaceContainer = Color(getColor(android.R.color.system_neutral1_800)), - onBackground = Color(getColor(android.R.color.system_neutral1_100)), - onSurfaceVariant = Color(getColor(android.R.color.system_neutral2_400)), - icon = Color(getColor(android.R.color.system_neutral1_100)), - primary = Color(getColor(android.R.color.system_accent1_200)), + background = colorResource(android.R.color.system_neutral1_900), + surfaceVariant = colorResource(android.R.color.system_neutral1_800), + surface = colorResource(android.R.color.system_neutral1_800), + surfaceContainer = colorResource(android.R.color.system_neutral1_800), + onBackground = colorResource(android.R.color.system_neutral1_100), + onSurfaceVariant = colorResource(android.R.color.system_neutral2_400), + icon = colorResource(android.R.color.system_neutral1_100), + primary = colorResource(android.R.color.system_accent1_200), ongoing = CloudStreamPalette.Ongoing, isLight = false, ) } else { CloudStreamColorScheme( - background = Color(getColor(android.R.color.system_neutral1_10)), - surfaceVariant = Color(getColor(android.R.color.system_neutral1_100)), - surface = Color(getColor(android.R.color.system_neutral1_100)), - surfaceContainer = Color(getColor(android.R.color.system_neutral1_100)), - onBackground = Color(getColor(android.R.color.system_neutral1_900)), - onSurfaceVariant = Color(getColor(android.R.color.system_neutral2_600)), - icon = Color(getColor(android.R.color.system_neutral1_900)), - primary = Color(getColor(android.R.color.system_accent1_600)), + background = colorResource(android.R.color.system_neutral1_10), + surfaceVariant = colorResource(android.R.color.system_neutral1_100), + surface = colorResource(android.R.color.system_neutral1_100), + surfaceContainer = colorResource(android.R.color.system_neutral1_100), + onBackground = colorResource(android.R.color.system_neutral1_900), + onSurfaceVariant = colorResource(android.R.color.system_neutral2_600), + icon = colorResource(android.R.color.system_neutral1_900), + primary = colorResource(android.R.color.system_accent1_600), ongoing = CloudStreamPalette.Ongoing, isLight = true, ) From c068e47ac56e3e22a88812940ac2989936b0ae87 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 14:52:54 -0600 Subject: [PATCH 041/122] -comment for tvFocusable --- .../lagradost/cloudstream4/ui/components/TvFocusableTest.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt index b3630b5a1f8..5d80bddcae3 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusableTest.kt @@ -15,12 +15,6 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -/** - * Covers the two distinct interaction models of [Modifier.tvFocusable]: - * - Phone/touch (`isTV = false`): behaves like a normal clickable, one tap = one click. - * - TV (`isTV = true`): the first tap only requests focus, a second tap while - * focused is what actually invokes [onClick] (mirrors D-pad "select" behavior). - */ @OptIn(ExperimentalTestApi::class) class TvFocusableTest { From ca77d6aed6e2635ce812f6dc810e81ce0a91138e Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 14:54:01 -0600 Subject: [PATCH 042/122] -optin --- .../cloudstream4/ui/components/settings/SettingsItemTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt index 115e95d8308..ca4818c60b7 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt @@ -15,7 +15,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -@OptIn(ExperimentalTestApi::class) +// @OptIn(ExperimentalTestApi::class) class SettingsItemTest { @BeforeTest From feb000a072d699834f81fe77a182bc63babfe5e6 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 14:56:05 -0600 Subject: [PATCH 043/122] + --- .../cloudstream4/ui/components/settings/SettingsItemTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt index ca4818c60b7..115e95d8308 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItemTest.kt @@ -15,7 +15,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -// @OptIn(ExperimentalTestApi::class) +@OptIn(ExperimentalTestApi::class) class SettingsItemTest { @BeforeTest From ef912186cc3fde01dbccd0b1d2f5041e973793b6 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:08:35 -0600 Subject: [PATCH 044/122] ReadOnlyComposable --- .../cloudstream4/ui/theme/DynamicTheme.android.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt index 0f6574924b3..4aa8936f356 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt @@ -1,11 +1,14 @@ package com.lagradost.cloudstream4.ui.theme import android.os.Build +import androidx.annotation.RequiresApi import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.res.colorResource @Composable +@ReadOnlyComposable actual fun resolveDynamicTheme(): CloudStreamColorScheme { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) buildMonetScheme() @@ -13,9 +16,10 @@ actual fun resolveDynamicTheme(): CloudStreamColorScheme { } @Composable +@ReadOnlyComposable +@RequiresApi(Build.VERSION_CODES.S) private fun buildMonetScheme(): CloudStreamColorScheme { - val isSystemDark = isSystemInDarkTheme() - return if (isSystemDark) { + return if (isSystemInDarkTheme()) { CloudStreamColorScheme( background = colorResource(android.R.color.system_neutral1_900), surfaceVariant = colorResource(android.R.color.system_neutral1_800), From fb5baeac69eb590a14f803dfaab3ce325489b0c1 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:10:20 -0600 Subject: [PATCH 045/122] ReadOnlyComposable --- .../cloudstream4/ui/theme/DynamicPrimaryColor.android.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt index e46218fd459..a703d83c7b1 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt @@ -1,11 +1,14 @@ package com.lagradost.cloudstream4.ui.theme import android.os.Build +import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.colorResource @Composable +@ReadOnlyComposable actual fun resolveDynamicPrimaryColor(): Color { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) colorResource(android.R.color.system_accent1_200) @@ -13,6 +16,7 @@ actual fun resolveDynamicPrimaryColor(): Color { } @Composable +@ReadOnlyComposable actual fun resolveDynamicSecondaryColor(): Color { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) colorResource(android.R.color.system_accent2_200) From c3c8fd22e43521a9eb24c0770df8dfecf7f359f2 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:11:54 -0600 Subject: [PATCH 046/122] ReadOnlyComposable --- .../kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.kt index 04509c3125b..da6950cde59 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.kt @@ -1,6 +1,8 @@ package com.lagradost.cloudstream4.ui.theme import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable @Composable +@ReadOnlyComposable expect fun resolveDynamicTheme(): CloudStreamColorScheme From 927146a3b538793d4fd5be9eaa0e08a8874b4ac0 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:12:30 -0600 Subject: [PATCH 047/122] ReadOnlyComposable --- .../com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.kt index 4fcc37a89e7..3ea327a4db0 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.kt @@ -1,10 +1,13 @@ package com.lagradost.cloudstream4.ui.theme import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.graphics.Color @Composable +@ReadOnlyComposable expect fun resolveDynamicPrimaryColor(): Color @Composable +@ReadOnlyComposable expect fun resolveDynamicSecondaryColor(): Color From 2e51a1df2841c00d80e4269d3ffaaf1d66870699 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:14:01 -0600 Subject: [PATCH 048/122] ReadOnlyComposable --- .../lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.jvm.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.jvm.kt b/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.jvm.kt index 392353ae30b..fc79a47dd02 100644 --- a/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.jvm.kt +++ b/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.jvm.kt @@ -1,10 +1,13 @@ package com.lagradost.cloudstream4.ui.theme import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.graphics.Color @Composable +@ReadOnlyComposable actual fun resolveDynamicPrimaryColor(): Color = CloudStreamPrimaryColor.NORMAL.color @Composable +@ReadOnlyComposable actual fun resolveDynamicSecondaryColor(): Color = CloudStreamPrimaryColor.NORMAL.color From c1f31af5c19f772764aeb4a463ab21fd7cfea30f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:14:32 -0600 Subject: [PATCH 049/122] ReadOnlyComposable --- .../com/lagradost/cloudstream4/ui/theme/DynamicTheme.jvm.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.jvm.kt b/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.jvm.kt index dd1330853ed..3534e2ce1a8 100644 --- a/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.jvm.kt +++ b/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.jvm.kt @@ -1,7 +1,9 @@ package com.lagradost.cloudstream4.ui.theme import androidx.compose.runtime.Composable +import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.graphics.Color @Composable +@ReadOnlyComposable actual fun resolveDynamicTheme(): CloudStreamColorScheme = darkScheme() From 755a7434c0b42ef451fbbba37a0258bd394c3d62 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:30:34 -0600 Subject: [PATCH 050/122] Add lint --- gradle/libs.versions.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ce4cfb0750f..f9939e7abe9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -12,11 +12,13 @@ coil = { strictly = "3.3.0" } # Later versions require jvmTarget 11 or later colorpicker = "6b46b49" composeMaterial3 = "1.11.0-alpha07" composeMultiplatform = "1.11.1" +composeRules = "0.6.2" conscryptAndroid = { strictly = "2.5.2" } # 2.5.3 crashes everything constraintlayout = "2.2.1" coreKtx = "1.18.0" cryptography = "0.6.0" desugar_jdk_libs_nio = "2.1.5" +detekt = "1.23.8" dokkaGradlePlugin = "2.2.0" espressoCore = "3.7.0" fragmentKtx = "1.8.9" @@ -81,6 +83,7 @@ colorpicker = { module = "com.github.recloudstream:color-picker-android", versio compose-foundation = { module = "org.jetbrains.compose.foundation:foundation", version.ref = "composeMultiplatform" } compose-material3 = { module = "org.jetbrains.compose.material3:material3", version.ref = "composeMaterial3" } compose-resources = { module = "org.jetbrains.compose.components:components-resources", version.ref = "composeMultiplatform" } +compose-rules-detekt = { module = "io.nlopez.compose.rules:detekt", version.ref = "composeRules" } compose-runtime = { module = "org.jetbrains.compose.runtime:runtime", version.ref = "composeMultiplatform" } compose-ui = { module = "org.jetbrains.compose.ui:ui", version.ref = "composeMultiplatform" } compose-ui-test = { module = "org.jetbrains.compose.ui:ui-test", version.ref = "composeMultiplatform" } @@ -94,6 +97,7 @@ cryptography-core = { module = "dev.whyoleg.cryptography:cryptography-core", ver cryptography-provider-optimal = { module = "dev.whyoleg.cryptography:cryptography-provider-optimal", version.ref = "cryptography" } databinding = { module = "androidx.databinding:viewbinding", version.ref = "androidGradlePlugin" } desugar_jdk_libs_nio = { module = "com.android.tools:desugar_jdk_libs_nio", version.ref = "desugar_jdk_libs_nio" } +detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" } ext-junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" } fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" } @@ -154,6 +158,7 @@ android-multiplatform-library = { id = "com.android.kotlin.multiplatform.library buildkonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildkonfigGradlePlugin" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlinGradlePlugin" } compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" } +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokkaGradlePlugin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm" , version.ref = "kotlinGradlePlugin" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlinGradlePlugin" } From f8a51806438aa56ec8418767de835d3250b7b4a1 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:31:30 -0600 Subject: [PATCH 051/122] Add detekt --- composeApp/build.gradle.kts | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 73d657376a1..9024623a37e 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -4,6 +4,7 @@ plugins { alias(libs.plugins.android.multiplatform.library) alias(libs.plugins.compose.multiplatform) alias(libs.plugins.compose.compiler) + alias(libs.plugins.detekt) } kotlin { @@ -16,6 +17,11 @@ kotlin { androidResources { enable = true } + + lint { + checkTestSources = true + checkDependencies = true + } } jvm() @@ -61,8 +67,23 @@ kotlin { } } +detekt { + buildUponDefaultConfig = true + config.setFrom(files("$rootDir/config/detekt/detekt.yml")) + source.setFrom( + "src/commonMain/kotlin", + "src/androidMain/kotlin", + "src/jvmMain/kotlin", + "src/commonTest/kotlin", + "src/jvmTest/kotlin", + ) +} + dependencies { androidRuntimeClasspath(libs.compose.ui.tooling) + + detektPlugins(libs.detekt.formatting) + detektPlugins(libs.compose.rules.detekt) } compose.resources { From c59e3f113cf4166f88f464cb5ad8647197601de7 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:32:26 -0600 Subject: [PATCH 052/122] Add --- build.gradle.kts | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle.kts b/build.gradle.kts index baf721a82cc..ed2df766074 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -5,6 +5,7 @@ plugins { alias(libs.plugins.buildkonfig) apply false // Universal build config alias(libs.plugins.compose.compiler) apply false alias(libs.plugins.compose.multiplatform) apply false + alias(libs.plugins.detekt) apply false alias(libs.plugins.dokka) apply false alias(libs.plugins.kotlin.jvm) apply false alias(libs.plugins.kotlin.multiplatform) apply false From e5b0dba897c234df54cf900174d19d4420605a9a Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:34:49 -0600 Subject: [PATCH 053/122] Add config --- config/detekt/detekt.yml | 54 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 config/detekt/detekt.yml diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml new file mode 100644 index 00000000000..7bb567721ea --- /dev/null +++ b/config/detekt/detekt.yml @@ -0,0 +1,54 @@ +build: + maxIssues: 0 + +comments: + active: false + +complexity: + LongMethod: + active: true + threshold: 60 + LongParameterList: + active: true + functionThreshold: 8 + constructorThreshold: 10 + TooManyFunctions: + active: true + thresholdInFiles: 30 + CyclomaticComplexMethod: + active: true + threshold: 20 + +style: + MagicNumber: + active: false + UnusedImports: + active: true + UnusedPrivateMember: + active: true + MaxLineLength: + active: true + maxLineLength: 120 + +potential-bugs: + active: true + +coroutines: + active: true + GlobalCoroutineUsage: + active: true + +naming: + active: true + +Compose: + ComposableFunctionNaming: + active: true + CompositionLocalAllowlist: + active: false + ModifierMissing: + active: true + ModifierReused: + active: true + UnstableCollections: + active: true From fb36a179288fa7a27a5d1b9a0c1bc680ae773880 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:43:01 -0600 Subject: [PATCH 054/122] rules --- config/detekt/detekt.yml | 56 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 7bb567721ea..02df2233a64 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -42,13 +42,67 @@ naming: active: true Compose: - ComposableFunctionNaming: + ComposableAnnotationNaming: + active: true + ComposableNaming: + active: true + ComposableParamOrder: active: true CompositionLocalAllowlist: active: false + CompositionLocalNaming: + active: true + ContentEmitterReturningValues: + active: true + ContentTrailingLambda: + active: true + ContentSlotReused: + active: true + DefaultsVisibility: + active: true + LambdaParameterEventTrailing: + active: true + LambdaParameterInRestartableEffect: + active: true + Material2: + active: false + ModifierClickableOrder: + active: true + ModifierComposed: + active: true ModifierMissing: active: true + ModifierNaming: + active: true + ModifierNotUsedAtRoot: + active: true ModifierReused: active: true + ModifierWithoutDefault: + active: true + MultipleEmitters: + active: true + MutableParams: + active: true + MutableStateAutoboxing: + active: true + MutableStateParam: + active: true + ParameterNaming: + active: true + PreviewAnnotationNaming: + active: true + PreviewNaming: + active: false + PreviewPublic: + active: true + RememberMissing: + active: true + RememberContentMissing: + active: true UnstableCollections: active: true + ViewModelForwarding: + active: true + ViewModelInjection: + active: true From 924c2ba5a3829a74a4ec587555b0dd4be8600889 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:47:08 -0600 Subject: [PATCH 055/122] exclude icons --- config/detekt/detekt.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 02df2233a64..3a726c038ef 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -1,6 +1,9 @@ build: maxIssues: 0 +config: + excludes: ['**/ui/icons/**'] + comments: active: false From 751295d28dd6b669ad014fce019a1f25092535ef Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:49:47 -0600 Subject: [PATCH 056/122] Fix --- config/detekt/detekt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 3a726c038ef..e49b405fd99 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -2,7 +2,7 @@ build: maxIssues: 0 config: - excludes: ['**/ui/icons/**'] + excludes: '**/ui/icons/**' comments: active: false From a4b99acf57b24c6f58241c15abc68e2b808d320c Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:56:57 -0600 Subject: [PATCH 057/122] Fix --- config/detekt/detekt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index e49b405fd99..7d7b54fe8d5 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -2,7 +2,7 @@ build: maxIssues: 0 config: - excludes: '**/ui/icons/**' + excludes: '.*/ui/icons/.*' comments: active: false From 31e2565a39c8f7cf17cd072015cab0cdddc40300 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 15:59:34 -0600 Subject: [PATCH 058/122] Update --- config/detekt/detekt.yml | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 7d7b54fe8d5..393017ac19c 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -1,27 +1,13 @@ -build: - maxIssues: 0 - config: + validation: true excludes: '.*/ui/icons/.*' +build: + maxIssues: 0 + comments: active: false -complexity: - LongMethod: - active: true - threshold: 60 - LongParameterList: - active: true - functionThreshold: 8 - constructorThreshold: 10 - TooManyFunctions: - active: true - thresholdInFiles: 30 - CyclomaticComplexMethod: - active: true - threshold: 20 - style: MagicNumber: active: false From 7b889d68e356319d9af02f4380f9f759085bf761 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:05:54 -0600 Subject: [PATCH 059/122] exclude indentation --- config/detekt/detekt.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 393017ac19c..dbc8fd50581 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -1,6 +1,5 @@ config: validation: true - excludes: '.*/ui/icons/.*' build: maxIssues: 0 @@ -8,6 +7,22 @@ build: comments: active: false +complexity: + LongMethod: + active: true + threshold: 60 + LongParameterList: + active: false + TooManyFunctions: + active: false + CyclomaticComplexMethod: + active: false + +formatting: + Indentation: + active: true + excludes: ['**/ui/icons/**'] + style: MagicNumber: active: false From bab8ec0c8786bf7be94b42993a2a33f8ee8ddb18 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:09:13 -0600 Subject: [PATCH 060/122] Disable MultiLineIfElse --- config/detekt/detekt.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index dbc8fd50581..9e751e2afac 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -33,6 +33,8 @@ style: MaxLineLength: active: true maxLineLength: 120 + MultiLineIfElse: + active: false potential-bugs: active: true From 691b938ca5eb105c1876a783254af0325ff02fe3 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:10:07 -0600 Subject: [PATCH 061/122] - --- .../com/lagradost/cloudstream4/ui/components/ProfilePicture.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt index b1851ed5747..6b3c0c8374f 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt @@ -29,7 +29,7 @@ import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource enum class ProfileImage { - DARK_BLUE, BLUE, ORANGE, PINK, PURPLE, RED, TEAL; + DARK_BLUE, BLUE, ORANGE, PINK, PURPLE, RED, TEAL, } @Composable From 13ade8ff965fe3327dba8f2ac667fecba4a96adc Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:12:10 -0600 Subject: [PATCH 062/122] Disable SpacingBetweenDeclarationsWithComments --- config/detekt/detekt.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 9e751e2afac..ae1dda72e4d 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -35,6 +35,8 @@ style: maxLineLength: 120 MultiLineIfElse: active: false + SpacingBetweenDeclarationsWithComments: + active: false potential-bugs: active: true From 89ff2271df8c6559c7baeb13a1b75f256ed9b110 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:13:36 -0600 Subject: [PATCH 063/122] Fix --- config/detekt/detekt.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index ae1dda72e4d..8d8fda9b284 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -22,6 +22,8 @@ formatting: Indentation: active: true excludes: ['**/ui/icons/**'] + MultiLineIfElse: + active: false style: MagicNumber: @@ -33,8 +35,6 @@ style: MaxLineLength: active: true maxLineLength: 120 - MultiLineIfElse: - active: false SpacingBetweenDeclarationsWithComments: active: false From 577d6566ce8ec20a78805ebf5105c4565271c350 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:14:11 -0600 Subject: [PATCH 064/122] Fix --- config/detekt/detekt.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 8d8fda9b284..9ba12cd9cbc 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -24,6 +24,8 @@ formatting: excludes: ['**/ui/icons/**'] MultiLineIfElse: active: false + SpacingBetweenDeclarationsWithComments: + active: false style: MagicNumber: @@ -35,8 +37,6 @@ style: MaxLineLength: active: true maxLineLength: 120 - SpacingBetweenDeclarationsWithComments: - active: false potential-bugs: active: true From 0767e365bfcadf95274501c4a0ca9f16f95c0fa2 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:17:29 -0600 Subject: [PATCH 065/122] no wildcard import --- .../ui/screens/settings/SettingsScreen.kt | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt index eabdd9f49a6..2471003beed 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt @@ -4,11 +4,27 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.WindowInsetsSides +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.only +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester From 27b176f1c92e57f21550af3dadd5ebf57cc6a86e Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:18:26 -0600 Subject: [PATCH 066/122] Same --- .../ui/components/settings/SettingsItem.kt | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt index 6909a9689f2..a5cd9c5113c 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt @@ -1,9 +1,19 @@ package com.lagradost.cloudstream4.ui.components.settings import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.* -import androidx.compose.material3.* -import androidx.compose.runtime.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.weight +import androidx.compose.foundation.layout.width +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester From cbe21eb976ba8461a8d181b46a1ae57d130ac716 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:20:23 -0600 Subject: [PATCH 067/122] no wildcard --- .../com/lagradost/cloudstream4/ui/components/TvFocusable.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusable.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusable.kt index 3cbf605b050..de21b03db2d 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusable.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusable.kt @@ -7,7 +7,11 @@ import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.PressInteraction import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester From f7f4d72df45eb49e8e150c322498901ffb8e0881 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:21:37 -0600 Subject: [PATCH 068/122] No wildcard --- .../cloudstream4/ui/theme/CloudStreamColorScheme.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorScheme.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorScheme.kt index 75914ce8c1b..3f309afdc0a 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorScheme.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorScheme.kt @@ -1,6 +1,10 @@ package com.lagradost.cloudstream4.ui.theme -import androidx.compose.runtime.* +import androidx.compose.runtime.Stable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.runtime.structuralEqualityPolicy import androidx.compose.ui.graphics.Color /** From 43ec0eeaaa2e71491e75e43eb3868d83ec091bfb Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:22:28 -0600 Subject: [PATCH 069/122] No wildcard --- .../com/lagradost/cloudstream4/ui/theme/CloudStreamTheme.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamTheme.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamTheme.kt index 20f19faa3b5..fdf069d488a 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamTheme.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamTheme.kt @@ -4,7 +4,11 @@ import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material3.MaterialTheme import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.ReadOnlyComposable +import androidx.compose.runtime.remember +import androidx.compose.runtime.staticCompositionLocalOf import androidx.compose.ui.graphics.Color val LocalCloudStreamColors = staticCompositionLocalOf { darkScheme() } From 8065976607dc8d41fbcb81d0ab8388f17b634a57 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:25:04 -0600 Subject: [PATCH 070/122] - --- .../cloudstream4/ui/components/settings/SettingsItem.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt index a5cd9c5113c..588cdb7861f 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt @@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.weight import androidx.compose.foundation.layout.width import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme From 329b5c858179312eb6d3b5c95abb316dd7fee07f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:26:20 -0600 Subject: [PATCH 071/122] Fix --- .../cloudstream4/ui/screens/settings/SettingsScreen.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt index 2471003beed..35f80885b21 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt @@ -16,6 +16,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.only import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawing +import androidx.compose.foundation.layout.statusBars import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.rememberScrollState From 1e726e6a08cfd4ca66bedf7903250b1a4f9012a1 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:27:08 -0600 Subject: [PATCH 072/122] - --- .../kotlin/com/lagradost/cloudstream4/utils/DeviceLayout.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/utils/DeviceLayout.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/utils/DeviceLayout.kt index d3364868802..20bb69ec2be 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/utils/DeviceLayout.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/utils/DeviceLayout.kt @@ -21,7 +21,7 @@ object DeviceLayout { /** * Returns true if the layout is any of the flags, so - * so isLayout(TV or EMULATOR) is a valid statement + * so isLayout(TV or EMULATOR) is a valid statement * for checking if the layout is in the emulator * or tv. Auto will become the "TV" or the * "PHONE" layout. From 5a48c8029ae466c68abb46da4310418a6af48813 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:31:37 -0600 Subject: [PATCH 073/122] Update --- .../lagradost/cloudstream4/utils/DeviceInfo.android.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/utils/DeviceInfo.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/utils/DeviceInfo.android.kt index 15c9186c5e4..9942ba6dc49 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/utils/DeviceInfo.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/utils/DeviceInfo.android.kt @@ -16,11 +16,11 @@ internal actual object DeviceInfo { val isTelevisionMode = uiModeManager?.currentModeType == Configuration.UI_MODE_TYPE_TELEVISION val model = Build.MODEL.lowercase() return when { - isTelevisionMode - || Build.MODEL.contains("AFT") // AFT = Fire TV - || model.contains("firestick") - || model.contains("fire tv") - || model.contains("chromecast") -> DeviceLayout.TV + isTelevisionMode || + Build.MODEL.contains("AFT") || // AFT = Fire TV + model.contains("firestick") || + model.contains("fire tv") || + model.contains("chromecast") -> DeviceLayout.TV else -> DeviceLayout.PHONE } } From f6a83ab7fff6c96f737aa38db52567a9cf191369 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:32:13 -0600 Subject: [PATCH 074/122] - --- .../cloudstream4/ui/theme/DynamicPrimaryColor.android.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt index a703d83c7b1..b496a2d2b61 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt @@ -1,7 +1,6 @@ package com.lagradost.cloudstream4.ui.theme import android.os.Build -import androidx.annotation.RequiresApi import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.ui.graphics.Color From ca295062cca098c2310399ff42d30bbe97c382f0 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:32:57 -0600 Subject: [PATCH 075/122] - --- .../com/lagradost/cloudstream4/ui/theme/DynamicTheme.jvm.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.jvm.kt b/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.jvm.kt index 3534e2ce1a8..b8b52080591 100644 --- a/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.jvm.kt +++ b/composeApp/src/jvmMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.jvm.kt @@ -2,7 +2,6 @@ package com.lagradost.cloudstream4.ui.theme import androidx.compose.runtime.Composable import androidx.compose.runtime.ReadOnlyComposable -import androidx.compose.ui.graphics.Color @Composable @ReadOnlyComposable From 20673da7e075907e885dc1e3f8329b6f9a0621b6 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:34:13 -0600 Subject: [PATCH 076/122] Fix --- .../cloudstream4/ui/theme/CloudStreamThemeModeTest.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeModeTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeModeTest.kt index 388f0d7130d..cfe893d2e33 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeModeTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamThemeModeTest.kt @@ -14,8 +14,16 @@ class CloudStreamThemeModeTest { @Test fun containsAllExpectedModesByName() { val expected = setOf( - "Dark", "Amoled", "Light", "Dracula", "Lavender", "SilentBlue", "FollowSystem", "Dynamic", + "Dark", + "Amoled", + "Light", + "Dracula", + "Lavender", + "SilentBlue", + "FollowSystem", + "Dynamic", ) + val actual = CloudStreamThemeMode.entries.map { it.name }.toSet() assertEquals(expected, actual) } From 14b3bafc4c069cd2a2fc1cf8697b9317ae0d8a28 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:34:53 -0600 Subject: [PATCH 077/122] Fix --- .../cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt index 0f77213c5a8..5b3affb2063 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamColorSchemeTest.kt @@ -63,8 +63,13 @@ class CloudStreamColorSchemeTest { // Every preset (aside from copies that explicitly override it) shares the // same default primary/ongoing accent colors from the palette. val schemes = listOf( - darkScheme(), lightScheme(), draculaScheme(), lavenderScheme(), silentBlueScheme(), + darkScheme(), + lightScheme(), + draculaScheme(), + lavenderScheme(), + silentBlueScheme(), ) + for (scheme in schemes) { assertEquals(CloudStreamPalette.Primary, scheme.primary) assertEquals(CloudStreamPalette.Ongoing, scheme.ongoing) From 6dfe2e6de8f1d0000fe9c64f9071d815a0e28df3 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:40:39 -0600 Subject: [PATCH 078/122] Update rules --- config/detekt/detekt.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 9ba12cd9cbc..b41684b49e7 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -48,6 +48,11 @@ coroutines: naming: active: true + FunctionNaming: + active: true + ignoreAnnotated: ['Composable'] + MatchingDeclarationName: + active: false Compose: ComposableAnnotationNaming: From bab11d07bf60a5ec447654981adacc0349c142f1 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:43:43 -0600 Subject: [PATCH 079/122] Update --- config/detekt/detekt.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index b41684b49e7..f998d4064ef 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -30,13 +30,14 @@ formatting: style: MagicNumber: active: false + MaxLineLength: + active: true + maxLineLength: 120 UnusedImports: active: true UnusedPrivateMember: active: true - MaxLineLength: - active: true - maxLineLength: 120 + ignoreAnnotated: ['Preview'] potential-bugs: active: true From 70b11ec37ef5eb6fd608c9d7b530f6d13732acf5 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:45:18 -0600 Subject: [PATCH 080/122] - --- config/detekt/detekt.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index f998d4064ef..6a1e68ae7d1 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -28,6 +28,8 @@ formatting: active: false style: + ForbiddenComment: + active: false MagicNumber: active: false MaxLineLength: From f5ecf14a4c48e333e284c8c22da212e72771d2e0 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:48:19 -0600 Subject: [PATCH 081/122] Enable --- config/detekt/detekt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 6a1e68ae7d1..69690ee4cfa 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -109,7 +109,7 @@ Compose: PreviewAnnotationNaming: active: true PreviewNaming: - active: false + active: true PreviewPublic: active: true RememberMissing: From 9192efad0576e8ff9d3fca7af60ccfd67a0e9ee5 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:49:29 -0600 Subject: [PATCH 082/122] suffix --- config/detekt/detekt.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 69690ee4cfa..57755c6003e 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -110,6 +110,7 @@ Compose: active: true PreviewNaming: active: true + previewNamingStrategy: suffix PreviewPublic: active: true RememberMissing: From 0e8274bf762b5ed9f95803e29819e094ff97ac4b Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 16:56:33 -0600 Subject: [PATCH 083/122] Update --- config/detekt/detekt.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 57755c6003e..2fa99f080be 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -58,6 +58,7 @@ naming: active: false Compose: + active: true ComposableAnnotationNaming: active: true ComposableNaming: @@ -65,7 +66,7 @@ Compose: ComposableParamOrder: active: true CompositionLocalAllowlist: - active: false + active: true CompositionLocalNaming: active: true ContentEmitterReturningValues: @@ -110,7 +111,6 @@ Compose: active: true PreviewNaming: active: true - previewNamingStrategy: suffix PreviewPublic: active: true RememberMissing: From 7460fb6a58220e3cc7dbe9734e374ae13f065dfc Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:01:47 -0600 Subject: [PATCH 084/122] test --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f9939e7abe9..6a6b4a72eb1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,7 @@ constraintlayout = "2.2.1" coreKtx = "1.18.0" cryptography = "0.6.0" desugar_jdk_libs_nio = "2.1.5" -detekt = "1.23.8" +detekt = "2.0.0-alpha.5" dokkaGradlePlugin = "2.2.0" espressoCore = "3.7.0" fragmentKtx = "1.8.9" From 64ab19b5d4122db14f303a697779fe43b2266e32 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:04:38 -0600 Subject: [PATCH 085/122] - --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6a6b4a72eb1..f9939e7abe9 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,7 @@ constraintlayout = "2.2.1" coreKtx = "1.18.0" cryptography = "0.6.0" desugar_jdk_libs_nio = "2.1.5" -detekt = "2.0.0-alpha.5" +detekt = "1.23.8" dokkaGradlePlugin = "2.2.0" espressoCore = "3.7.0" fragmentKtx = "1.8.9" From 6cece3a7816193e11586c3912b3fc26631c78a98 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 6 Jul 2026 10:43:04 -0600 Subject: [PATCH 086/122] WildcardImport rule --- config/detekt/detekt.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 2fa99f080be..5ecefbc27a5 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -40,6 +40,9 @@ style: UnusedPrivateMember: active: true ignoreAnnotated: ['Preview'] + WildcardImport: + active: true + excludeImports: [] potential-bugs: active: true From 3d900f5da8c8e0878ea04e8549a0bfd99b439adc Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 6 Jul 2026 10:49:13 -0600 Subject: [PATCH 087/122] Add --- config/detekt/detekt.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 5ecefbc27a5..d14e429c191 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -30,11 +30,20 @@ formatting: style: ForbiddenComment: active: false + ForbiddenImport: + active: true + forbiddenImports: + - value: 'kotlin.jvm.JvmField' + reason: 'Use explicit backing properties instead.' + - value: 'java.util.*' + reason: 'Use Kotlin standard library APIs instead.' MagicNumber: active: false MaxLineLength: active: true maxLineLength: 120 + SpacingAfterPackageAndImports: + active: true UnusedImports: active: true UnusedPrivateMember: From 09528213888b91cca516d189ba8e35a9cd71a018 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 6 Jul 2026 10:57:40 -0600 Subject: [PATCH 088/122] Fix --- config/detekt/detekt.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index d14e429c191..bc67d456415 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -32,7 +32,7 @@ style: active: false ForbiddenImport: active: true - forbiddenImports: + imports: # forbiddenImports - value: 'kotlin.jvm.JvmField' reason: 'Use explicit backing properties instead.' - value: 'java.util.*' @@ -42,7 +42,7 @@ style: MaxLineLength: active: true maxLineLength: 120 - SpacingAfterPackageAndImports: + SpacingAfterPackageDeclaration: # SpacingAfterPackageAndImports active: true UnusedImports: active: true From 172b66e63c3e8661638609f81e5d4cb846b865d5 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:00:12 -0600 Subject: [PATCH 089/122] Fix --- config/detekt/detekt.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index bc67d456415..b70ebd73355 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -42,7 +42,7 @@ style: MaxLineLength: active: true maxLineLength: 120 - SpacingAfterPackageDeclaration: # SpacingAfterPackageAndImports + SpacingBetweenPackageAndImports: # SpacingAfterPackageAndImports active: true UnusedImports: active: true From e06c467ee1a65a0030740387abc7a5abcb956f1d Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:10:59 -0600 Subject: [PATCH 090/122] 2.0 --- gradle/libs.versions.toml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f9939e7abe9..3577d51f46b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,7 +18,7 @@ constraintlayout = "2.2.1" coreKtx = "1.18.0" cryptography = "0.6.0" desugar_jdk_libs_nio = "2.1.5" -detekt = "1.23.8" +detekt = "2.0.0-alpha.5" dokkaGradlePlugin = "2.2.0" espressoCore = "3.7.0" fragmentKtx = "1.8.9" @@ -97,7 +97,7 @@ cryptography-core = { module = "dev.whyoleg.cryptography:cryptography-core", ver cryptography-provider-optimal = { module = "dev.whyoleg.cryptography:cryptography-provider-optimal", version.ref = "cryptography" } databinding = { module = "androidx.databinding:viewbinding", version.ref = "androidGradlePlugin" } desugar_jdk_libs_nio = { module = "com.android.tools:desugar_jdk_libs_nio", version.ref = "desugar_jdk_libs_nio" } -detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } +detekt-formatting = { module = "dev.detekt:detekt-rules-ktlint-wrapper", version.ref = "detekt" } espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" } ext-junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" } fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" } @@ -158,7 +158,7 @@ android-multiplatform-library = { id = "com.android.kotlin.multiplatform.library buildkonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildkonfigGradlePlugin" } compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlinGradlePlugin" } compose-multiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" } -detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +detekt = { id = "dev.detekt", version.ref = "detekt" } dokka = { id = "org.jetbrains.dokka", version.ref = "dokkaGradlePlugin" } kotlin-jvm = { id = "org.jetbrains.kotlin.jvm" , version.ref = "kotlinGradlePlugin" } kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlinGradlePlugin" } From ba5a60404cf86dad613f48db73e4a04ca4f6d4c9 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:24:10 -0600 Subject: [PATCH 091/122] Update rules for 2.0 --- config/detekt/detekt.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index b70ebd73355..cba87c656d0 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -1,16 +1,12 @@ config: validation: true -build: - maxIssues: 0 - comments: active: false complexity: LongMethod: - active: true - threshold: 60 + active: false LongParameterList: active: false TooManyFunctions: @@ -32,7 +28,7 @@ style: active: false ForbiddenImport: active: true - imports: # forbiddenImports + forbiddenImports: - value: 'kotlin.jvm.JvmField' reason: 'Use explicit backing properties instead.' - value: 'java.util.*' @@ -42,13 +38,15 @@ style: MaxLineLength: active: true maxLineLength: 120 - SpacingBetweenPackageAndImports: # SpacingAfterPackageAndImports + SpacingAfterPackageAndImports: active: true - UnusedImports: + UnusedImport: active: true - UnusedPrivateMember: + UnusedPrivateFunction: active: true ignoreAnnotated: ['Preview'] + UnusedPrivateProperty: + active: true WildcardImport: active: true excludeImports: [] From 51f3680ffdd0b3ee13213e1ab03b7f7820d4143f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:33:36 -0600 Subject: [PATCH 092/122] ktlint rule --- config/detekt/detekt.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index cba87c656d0..b0548e62799 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -14,7 +14,8 @@ complexity: CyclomaticComplexMethod: active: false -formatting: +ktlint: + active: true Indentation: active: true excludes: ['**/ui/icons/**'] From 5ad41a82e805a932ae785a214dd3431b40bb8792 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Mon, 6 Jul 2026 11:40:50 -0600 Subject: [PATCH 093/122] BackingPropertyNaming --- config/detekt/detekt.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index b0548e62799..526ef3af527 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -16,6 +16,9 @@ complexity: ktlint: active: true + BackingPropertyNaming: + active: true + excludes: ['**/ui/icons/**'] Indentation: active: true excludes: ['**/ui/icons/**'] From 85fff2538f2bd79a75c84ce03da3e050f0fd39d5 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 10:43:29 -0600 Subject: [PATCH 094/122] Updates to some rules --- config/detekt/detekt.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 526ef3af527..e2b3f2a5cbc 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -19,6 +19,10 @@ ktlint: BackingPropertyNaming: active: true excludes: ['**/ui/icons/**'] + ClassSignature: + active: false + FunctionSignature: + active: false Indentation: active: true excludes: ['**/ui/icons/**'] @@ -26,6 +30,8 @@ ktlint: active: false SpacingBetweenDeclarationsWithComments: active: false + TrailingCommaOnCallSite: + active: false style: ForbiddenComment: @@ -81,6 +87,7 @@ Compose: active: true CompositionLocalAllowlist: active: true + allowedCompositionLocals: LocalCloudStreamColors CompositionLocalNaming: active: true ContentEmitterReturningValues: From 0039fff2eb308710b551fb2c0c294838ec83caed Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 10:45:11 -0600 Subject: [PATCH 095/122] Split enum --- .../cloudstream4/ui/components/ProfilePicture.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt index 6b3c0c8374f..7bf5dfb7e9f 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt @@ -29,7 +29,13 @@ import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.stringResource enum class ProfileImage { - DARK_BLUE, BLUE, ORANGE, PINK, PURPLE, RED, TEAL, + BLUE, + DARK_BLUE, + ORANGE, + PINK, + PURPLE, + RED, + TEAL, } @Composable From a55c8fcf6e80156352bbb1a17737f552e541374f Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 10:51:57 -0600 Subject: [PATCH 096/122] rules --- config/detekt/detekt.yml | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index e2b3f2a5cbc..c7331583785 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -19,17 +19,25 @@ ktlint: BackingPropertyNaming: active: true excludes: ['**/ui/icons/**'] + BlankLineBetweenWhenConditions: + active: true + severity: info ClassSignature: active: false + FunctionExpressionBody: + active: true + severity: info FunctionSignature: active: false Indentation: active: true excludes: ['**/ui/icons/**'] MultiLineIfElse: - active: false + active: true + severity: info SpacingBetweenDeclarationsWithComments: - active: false + active: true + severity: info TrailingCommaOnCallSite: active: false From 3434ca434bfbd3f48a15d4b6320c19f9171b89b3 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 10:59:31 -0600 Subject: [PATCH 097/122] Temp --- config/detekt/detekt.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index c7331583785..c822695e0f0 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -20,12 +20,12 @@ ktlint: active: true excludes: ['**/ui/icons/**'] BlankLineBetweenWhenConditions: - active: true + active: false severity: info ClassSignature: active: false FunctionExpressionBody: - active: true + active: false severity: info FunctionSignature: active: false @@ -33,10 +33,10 @@ ktlint: active: true excludes: ['**/ui/icons/**'] MultiLineIfElse: - active: true + active: false severity: info SpacingBetweenDeclarationsWithComments: - active: true + active: false severity: info TrailingCommaOnCallSite: active: false From 6f82280f8a4f752558662c0f6eb98f512d6a4c43 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:03:49 -0600 Subject: [PATCH 098/122] Order --- .../cloudstream4/ui/components/settings/SettingsItem.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt index 588cdb7861f..5af5faf8627 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt @@ -31,12 +31,12 @@ import org.jetbrains.compose.resources.stringResource @Composable fun SettingsItem( + onClick: () -> Unit, title: String, modifier: Modifier = Modifier, subtitle: String? = null, icon: ImageVector? = null, focusRequester: FocusRequester? = null, - onClick: () -> Unit, ) { val colors = CloudStreamTheme.colors val isTV = remember { DeviceLayout.isLayout(DeviceLayout.TV) } From b9fc456ea154a1d51b49185439599aa12f07d20d Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:09:06 -0600 Subject: [PATCH 099/122] Add modifier param --- .../lagradost/cloudstream4/ui/components/ProfilePicture.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt index 7bf5dfb7e9f..b4a56ed174d 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt @@ -58,16 +58,18 @@ private fun ProfileImage.toRes(): DrawableResource = when (this) { * @param profileImage Local background image fallback * @param size Diameter of the circle, default 50.dp * @param profilePictureUrl Optional remote URL to load via Coil + * @param modifier Modifier to be applied to the profile picture container */ @Composable fun ProfilePicture( profileImage: ProfileImage, size: Dp = 50.dp, profilePictureUrl: String? = null, + modifier: Modifier = Modifier, ) { val colors = CloudStreamTheme.colors Box( - modifier = Modifier + modifier = modifier .size(size) .border(2.dp, colors.onBackground.copy(alpha = 0.2f), CircleShape) .clip(CircleShape), From 184bb850b3a72748d0c13c89ab8f6f22f374f5a7 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:14:24 -0600 Subject: [PATCH 100/122] Add modifier param --- .../cloudstream4/ui/screens/settings/SettingsScreen.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt index 35f80885b21..53268dd1364 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt @@ -128,6 +128,7 @@ fun SettingsScreen( version: SettingsVersionState, onNavigate: (SettingsCategory) -> Unit, onVersionLongClick: () -> Unit = {}, + modifier: Modifier = Modifier, ) { val colors = CloudStreamTheme.colors val isTV = remember { DeviceLayout.isLayout(DeviceLayout.TV) } @@ -138,7 +139,7 @@ fun SettingsScreen( } Box( - modifier = Modifier + modifier = modifier .fillMaxSize() .background(colors.background) .windowInsetsPadding(WindowInsets.statusBars) @@ -158,11 +159,11 @@ fun SettingsScreen( SettingsCategory.entries.forEachIndexed { index, category -> SettingsItem( + onClick = { onNavigate(category) }, title = category.label(), subtitle = category.subtitle(), icon = category.icon(), focusRequester = if (index == 0) firstItemFocusRequester else null, - onClick = { onNavigate(category) }, ) } From 7117f9cb238643ce4aae38842f8c9049caffcbf6 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:17:02 -0600 Subject: [PATCH 101/122] Order --- .../cloudstream4/ui/components/settings/SettingsItem.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt index 5af5faf8627..20ab25160e2 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt @@ -31,12 +31,12 @@ import org.jetbrains.compose.resources.stringResource @Composable fun SettingsItem( - onClick: () -> Unit, title: String, - modifier: Modifier = Modifier, subtitle: String? = null, icon: ImageVector? = null, focusRequester: FocusRequester? = null, + onClick: () -> Unit, + modifier: Modifier = Modifier, ) { val colors = CloudStreamTheme.colors val isTV = remember { DeviceLayout.isLayout(DeviceLayout.TV) } From 341fe919a4808d216f50c78219056f5a6b3ff0c3 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:17:41 -0600 Subject: [PATCH 102/122] Order --- .../cloudstream4/ui/screens/settings/SettingsScreen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt index 53268dd1364..438fd374574 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt @@ -159,11 +159,11 @@ fun SettingsScreen( SettingsCategory.entries.forEachIndexed { index, category -> SettingsItem( - onClick = { onNavigate(category) }, title = category.label(), subtitle = category.subtitle(), icon = category.icon(), focusRequester = if (index == 0) firstItemFocusRequester else null, + onClick = { onNavigate(category) }, ) } From 015d3d0572293a20f3d76f7452dbc14f35238b60 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:20:31 -0600 Subject: [PATCH 103/122] Order --- .../lagradost/cloudstream4/ui/components/ProfilePicture.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt index b4a56ed174d..9997a2264e4 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/ProfilePicture.kt @@ -56,16 +56,16 @@ private fun ProfileImage.toRes(): DrawableResource = when (this) { * otherwise falls back to the local [profileImage] background. * * @param profileImage Local background image fallback + * @param modifier Modifier to be applied to the profile picture container * @param size Diameter of the circle, default 50.dp * @param profilePictureUrl Optional remote URL to load via Coil - * @param modifier Modifier to be applied to the profile picture container */ @Composable fun ProfilePicture( profileImage: ProfileImage, + modifier: Modifier = Modifier, size: Dp = 50.dp, profilePictureUrl: String? = null, - modifier: Modifier = Modifier, ) { val colors = CloudStreamTheme.colors Box( From 55510ea90a76a44d9735148865b5ce8d168e1297 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:21:27 -0600 Subject: [PATCH 104/122] Fix --- .../cloudstream4/ui/components/settings/SettingsItem.kt | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt index 20ab25160e2..e7dfebf2d73 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/settings/SettingsItem.kt @@ -32,11 +32,11 @@ import org.jetbrains.compose.resources.stringResource @Composable fun SettingsItem( title: String, + modifier: Modifier = Modifier, subtitle: String? = null, icon: ImageVector? = null, focusRequester: FocusRequester? = null, - onClick: () -> Unit, - modifier: Modifier = Modifier, + onClick: () -> Unit = {}, ) { val colors = CloudStreamTheme.colors val isTV = remember { DeviceLayout.isLayout(DeviceLayout.TV) } @@ -89,7 +89,6 @@ private fun SettingsItemPreview() { title = stringResource(Res.string.category_general), subtitle = stringResource(Res.string.category_general_subtitle), icon = tune, - onClick = {}, ) } } @@ -101,7 +100,6 @@ private fun SettingsItemNoIconPreview() { SettingsItem( title = stringResource(Res.string.category_general), subtitle = stringResource(Res.string.category_general_subtitle), - onClick = {}, ) } } @@ -113,7 +111,6 @@ private fun SettingsItemNoSubtitlePreview() { SettingsItem( title = stringResource(Res.string.category_general), icon = tune, - onClick = {}, ) } } From 61e220b786cb093d4164d4d4d4a3022d0dadf125 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Tue, 7 Jul 2026 11:27:19 -0600 Subject: [PATCH 105/122] Composable --- .../cloudstream4/ui/components/CloudStreamRipple.kt | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/CloudStreamRipple.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/CloudStreamRipple.kt index 2d5b50363de..9f104f8bdff 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/CloudStreamRipple.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/CloudStreamRipple.kt @@ -3,19 +3,20 @@ package com.lagradost.cloudstream4.ui.components import androidx.compose.foundation.indication import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.material3.ripple +import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.composed import com.lagradost.cloudstream4.ui.theme.CloudStreamTheme /** * Applies a ripple indication styled to match the app's selected theme. */ +@Composable fun Modifier.cloudStreamRipple( interactionSource: MutableInteractionSource, bounded: Boolean = true, -): Modifier = composed { +): Modifier { val colors = CloudStreamTheme.colors - this.indication( + return this.indication( interactionSource = interactionSource, indication = ripple(bounded = bounded, color = colors.onBackground), ) From 00fb4c287779c1c08c3b5506b1d9da39cac5f4ab Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:20:14 -0600 Subject: [PATCH 106/122] + --- config/detekt/detekt.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index c822695e0f0..21c1cb02c7d 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -33,8 +33,7 @@ ktlint: active: true excludes: ['**/ui/icons/**'] MultiLineIfElse: - active: false - severity: info + active: true SpacingBetweenDeclarationsWithComments: active: false severity: info From 2758dfdfac5f35475ce55c387295513cbcc7fa08 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:24:10 -0600 Subject: [PATCH 107/122] - --- .../cloudstream4/ui/theme/CloudStreamPrimaryColorTest.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPrimaryColorTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPrimaryColorTest.kt index 0030a0391d6..d325a8c9fdf 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPrimaryColorTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/ui/theme/CloudStreamPrimaryColorTest.kt @@ -27,11 +27,6 @@ class CloudStreamPrimaryColorTest { } } - @Test - fun containsExpectedNumberOfColorOptions() { - assertEquals(22, CloudStreamPrimaryColor.entries.size) - } - @Test fun whiteEntryIsActuallyWhite() { val white = CloudStreamPrimaryColor.WHITE.color From 023dddea088f2ac3bf75db2c2098b21009d5274c Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:35:15 -0600 Subject: [PATCH 108/122] Update --- .../cloudstream4/ui/theme/DynamicPrimaryColor.android.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt index b496a2d2b61..df9162eda5b 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt @@ -9,15 +9,15 @@ import androidx.compose.ui.res.colorResource @Composable @ReadOnlyComposable actual fun resolveDynamicPrimaryColor(): Color { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { colorResource(android.R.color.system_accent1_200) - else CloudStreamPrimaryColor.NORMAL.color + } else CloudStreamPrimaryColor.NORMAL.color } @Composable @ReadOnlyComposable actual fun resolveDynamicSecondaryColor(): Color { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { colorResource(android.R.color.system_accent2_200) - else CloudStreamPrimaryColor.NORMAL.color + } else CloudStreamPrimaryColor.NORMAL.color } From a986da035889edbcd5658466904a5ae083a65ba9 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:35:49 -0600 Subject: [PATCH 109/122] Update --- .../lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt index 4aa8936f356..4e5010d5baf 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt @@ -10,9 +10,9 @@ import androidx.compose.ui.res.colorResource @Composable @ReadOnlyComposable actual fun resolveDynamicTheme(): CloudStreamColorScheme { - return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { buildMonetScheme() - else darkScheme() + } else darkScheme() } @Composable From 552fa3ffb2c59f5e1928ed03dfdc01a1af4b326a Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:37:22 -0600 Subject: [PATCH 110/122] Update --- .../cloudstream4/ui/theme/ThemePreferenceHelper.android.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt index e18e75edd9c..423fe732f5c 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt @@ -19,7 +19,7 @@ fun Context.loadThemeMode(): CloudStreamThemeMode { "SilentBlue" -> CloudStreamThemeMode.SilentBlue "Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { CloudStreamThemeMode.Dynamic - } else CloudStreamThemeMode.Dark + } else { CloudStreamThemeMode.Dark } else -> CloudStreamThemeMode.Dark } } @@ -49,10 +49,10 @@ fun Context.loadPrimaryColor(): CloudStreamPrimaryColor { "Lavender" -> CloudStreamPrimaryColor.LAVENDER "Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { CloudStreamPrimaryColor.DYNAMIC - } else CloudStreamPrimaryColor.NORMAL + } else { CloudStreamPrimaryColor.NORMAL } "Monet2" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { CloudStreamPrimaryColor.DYNAMIC_TWO - } else CloudStreamPrimaryColor.NORMAL + } else { CloudStreamPrimaryColor.NORMAL } else -> CloudStreamPrimaryColor.NORMAL } } From 1ed89c4fc347fd4cc633b111bf029a29202a687c Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:37:47 -0600 Subject: [PATCH 111/122] Update --- .../cloudstream4/ui/theme/DynamicPrimaryColor.android.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt index df9162eda5b..9635ef3c420 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt @@ -11,7 +11,7 @@ import androidx.compose.ui.res.colorResource actual fun resolveDynamicPrimaryColor(): Color { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { colorResource(android.R.color.system_accent1_200) - } else CloudStreamPrimaryColor.NORMAL.color + } else { CloudStreamPrimaryColor.NORMAL.color } } @Composable @@ -19,5 +19,5 @@ actual fun resolveDynamicPrimaryColor(): Color { actual fun resolveDynamicSecondaryColor(): Color { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { colorResource(android.R.color.system_accent2_200) - } else CloudStreamPrimaryColor.NORMAL.color + } else { CloudStreamPrimaryColor.NORMAL.color } } From 41a7cdd407043e7fc56a4e7bdbf533434699a70d Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:38:09 -0600 Subject: [PATCH 112/122] Update --- .../com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt index 4e5010d5baf..8afba0d5ec5 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.res.colorResource actual fun resolveDynamicTheme(): CloudStreamColorScheme { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { buildMonetScheme() - } else darkScheme() + } else { darkScheme() } } @Composable From a8691b08ef9fbd7a001a1ba01d3f258af43aa89e Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:39:28 -0600 Subject: [PATCH 113/122] - --- .../lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt index 8afba0d5ec5..b14857814a6 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicTheme.android.kt @@ -12,7 +12,9 @@ import androidx.compose.ui.res.colorResource actual fun resolveDynamicTheme(): CloudStreamColorScheme { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { buildMonetScheme() - } else { darkScheme() } + } else { + darkScheme() + } } @Composable From b521a33dce3398a5c06cb59aeb10c91442bfde78 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:39:57 -0600 Subject: [PATCH 114/122] - --- .../cloudstream4/ui/theme/DynamicPrimaryColor.android.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt index 9635ef3c420..4c77cc4b54a 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/DynamicPrimaryColor.android.kt @@ -11,7 +11,9 @@ import androidx.compose.ui.res.colorResource actual fun resolveDynamicPrimaryColor(): Color { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { colorResource(android.R.color.system_accent1_200) - } else { CloudStreamPrimaryColor.NORMAL.color } + } else { + CloudStreamPrimaryColor.NORMAL.color + } } @Composable @@ -19,5 +21,7 @@ actual fun resolveDynamicPrimaryColor(): Color { actual fun resolveDynamicSecondaryColor(): Color { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { colorResource(android.R.color.system_accent2_200) - } else { CloudStreamPrimaryColor.NORMAL.color } + } else { + CloudStreamPrimaryColor.NORMAL.color + } } From c50300eeb6e77ca900386537395dbadd0d9661fa Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:40:43 -0600 Subject: [PATCH 115/122] - --- .../ui/theme/ThemePreferenceHelper.android.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt index 423fe732f5c..d16276a216d 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt @@ -19,7 +19,9 @@ fun Context.loadThemeMode(): CloudStreamThemeMode { "SilentBlue" -> CloudStreamThemeMode.SilentBlue "Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { CloudStreamThemeMode.Dynamic - } else { CloudStreamThemeMode.Dark } + } else { + CloudStreamThemeMode.Dark + } else -> CloudStreamThemeMode.Dark } } @@ -52,7 +54,9 @@ fun Context.loadPrimaryColor(): CloudStreamPrimaryColor { } else { CloudStreamPrimaryColor.NORMAL } "Monet2" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { CloudStreamPrimaryColor.DYNAMIC_TWO - } else { CloudStreamPrimaryColor.NORMAL } + } else { + CloudStreamPrimaryColor.NORMAL + } else -> CloudStreamPrimaryColor.NORMAL } } From 8c75d0a100d5bd8052591b188fdefcad6a1292d1 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:42:03 -0600 Subject: [PATCH 116/122] Update --- .../com/lagradost/cloudstream4/ui/components/TvFocusable.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusable.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusable.kt index de21b03db2d..c87c4ee325d 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusable.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/components/TvFocusable.kt @@ -67,8 +67,7 @@ fun Modifier.tvFocusable( interactionSource.emit(PressInteraction.Release(press)) }, onTap = { - if (!isFocused) effectiveFocusRequester.requestFocus() - else onClick() + if (!isFocused) effectiveFocusRequester.requestFocus() else onClick() }, ) } From 59844df08d3b32d47b45ae4a606857b8f37d7bde Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:43:05 -0600 Subject: [PATCH 117/122] Update --- .../cloudstream4/ui/screens/settings/SettingsScreen.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt index 438fd374574..f070233dc5f 100644 --- a/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt +++ b/composeApp/src/commonMain/kotlin/com/lagradost/cloudstream4/ui/screens/settings/SettingsScreen.kt @@ -144,9 +144,13 @@ fun SettingsScreen( .background(colors.background) .windowInsetsPadding(WindowInsets.statusBars) .then( - if (isTV) Modifier.windowInsetsPadding( - WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal) - ) else Modifier + if (isTV) { + Modifier.windowInsetsPadding( + WindowInsets.safeDrawing.only(WindowInsetsSides.Horizontal) + ) + } else { + Modifier + } ), contentAlignment = Alignment.Center, ) { From ed169672f8ddcc0347b26080bb1e3b6328d552d9 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Fri, 10 Jul 2026 15:45:20 -0600 Subject: [PATCH 118/122] Update --- .../cloudstream4/ui/theme/ThemePreferenceHelper.android.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt index d16276a216d..2c59b675a33 100644 --- a/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt +++ b/composeApp/src/androidMain/kotlin/com/lagradost/cloudstream4/ui/theme/ThemePreferenceHelper.android.kt @@ -51,7 +51,9 @@ fun Context.loadPrimaryColor(): CloudStreamPrimaryColor { "Lavender" -> CloudStreamPrimaryColor.LAVENDER "Monet" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { CloudStreamPrimaryColor.DYNAMIC - } else { CloudStreamPrimaryColor.NORMAL } + } else { + CloudStreamPrimaryColor.NORMAL + } "Monet2" -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { CloudStreamPrimaryColor.DYNAMIC_TWO } else { From 9d12b20916354e25d9189f0e795386a776393e4d Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 11 Jul 2026 11:34:51 -0600 Subject: [PATCH 119/122] Update --- config/detekt/detekt.yml | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/config/detekt/detekt.yml b/config/detekt/detekt.yml index 21c1cb02c7d..78d3762167a 100644 --- a/config/detekt/detekt.yml +++ b/config/detekt/detekt.yml @@ -90,6 +90,8 @@ Compose: active: true ComposableNaming: active: true + ComposableNestingDepth: + active: true ComposableParamOrder: active: true CompositionLocalAllowlist: @@ -97,6 +99,8 @@ Compose: allowedCompositionLocals: LocalCloudStreamColors CompositionLocalNaming: active: true + ConditionHoist: + active: true ContentEmitterReturningValues: active: true ContentTrailingLambda: @@ -105,12 +109,16 @@ Compose: active: true DefaultsVisibility: active: true + InvalidReadOnlyComposable: + active: true LambdaParameterEventTrailing: active: true LambdaParameterInRestartableEffect: active: true Material2: active: false + MissingReadOnlyComposable: + active: true ModifierClickableOrder: active: true ModifierComposed: @@ -141,12 +149,20 @@ Compose: active: true PreviewPublic: active: true + RememberContentMissing: + active: true RememberMissing: active: true - RememberContentMissing: + StaleRememberUpdatedStateInRemember: + active: true + StateParam: + active: true + UnnecessaryComposable: active: true UnstableCollections: active: true + VarsWithoutStateBacking: + active: true ViewModelForwarding: active: true ViewModelInjection: From be88b1df38dee5cd73aadbeb1d0901a47895a671 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 11 Jul 2026 11:38:17 -0600 Subject: [PATCH 120/122] Update --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3577d51f46b..a3fe2e01b0d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -97,7 +97,7 @@ cryptography-core = { module = "dev.whyoleg.cryptography:cryptography-core", ver cryptography-provider-optimal = { module = "dev.whyoleg.cryptography:cryptography-provider-optimal", version.ref = "cryptography" } databinding = { module = "androidx.databinding:viewbinding", version.ref = "androidGradlePlugin" } desugar_jdk_libs_nio = { module = "com.android.tools:desugar_jdk_libs_nio", version.ref = "desugar_jdk_libs_nio" } -detekt-formatting = { module = "dev.detekt:detekt-rules-ktlint-wrapper", version.ref = "detekt" } +detekt-ktlint-wrapper = { module = "dev.detekt:detekt-rules-ktlint-wrapper", version.ref = "detekt" } espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" } ext-junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" } fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "fragmentKtx" } From 60d74fb91a8079f25c4ccaf9053e9f364bb59cf7 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sat, 11 Jul 2026 11:39:21 -0600 Subject: [PATCH 121/122] Update --- composeApp/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts index 9024623a37e..82562da54a3 100644 --- a/composeApp/build.gradle.kts +++ b/composeApp/build.gradle.kts @@ -82,7 +82,7 @@ detekt { dependencies { androidRuntimeClasspath(libs.compose.ui.tooling) - detektPlugins(libs.detekt.formatting) + detektPlugins(libs.detekt.ktlint.wrapper) detektPlugins(libs.compose.rules.detekt) } From e05de77b1cddb9d1b27c27a63c882562b714bae6 Mon Sep 17 00:00:00 2001 From: Luna712 <142361265+Luna712@users.noreply.github.com> Date: Sun, 12 Jul 2026 15:13:02 -0600 Subject: [PATCH 122/122] islayoutstate --- .../cloudstream4/utils/DeviceLayoutTest.kt | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt index bc4d570ef35..727b46e4b56 100644 --- a/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt +++ b/composeApp/src/commonTest/kotlin/com/lagradost/cloudstream4/utils/DeviceLayoutTest.kt @@ -66,4 +66,37 @@ class DeviceLayoutTest { // which we might add tests for eventually but not for now. DeviceLayout.isLandscape() } + + @Test + fun isLayoutStateMatchesSameFlagAsIsLayout() { + DeviceLayout.update() + val anyKnownLayout = + DeviceLayout.PHONE or DeviceLayout.TV or DeviceLayout.EMULATOR or DeviceLayout.COMPUTER + assertTrue(DeviceLayout.isLayoutState(anyKnownLayout).value) + assertTrue(DeviceLayout.isLayoutState(anyKnownLayout).value == DeviceLayout.isLayout(anyKnownLayout)) + } + + @Test + fun isLayoutStateDoesNotMatchUnrelatedFlagCombo() { + DeviceLayout.update() + // Whatever the resolved layout is, PHONE and TV are mutually + // exclusive single flags, so at most one of them can be active. + val phoneMatches = DeviceLayout.isLayoutState(DeviceLayout.PHONE).value + val tvMatches = DeviceLayout.isLayoutState(DeviceLayout.TV).value + assertFalse(phoneMatches && tvMatches) + } + + @Test + fun isLayoutStateReflectsUpdatesToLayoutState() { + // update() re-resolves and writes through to both layoutId and + // layoutState, so isLayoutState should always agree with isLayout + // immediately after any call to update(). + DeviceLayout.update() + val everything = + DeviceLayout.PHONE or DeviceLayout.TV or DeviceLayout.EMULATOR or DeviceLayout.COMPUTER + assertTrue(DeviceLayout.isLayoutState(everything).value == DeviceLayout.isLayout(everything)) + + DeviceLayout.update() + assertTrue(DeviceLayout.isLayoutState(everything).value == DeviceLayout.isLayout(everything)) + } }