init projects

This commit is contained in:
2026-06-17 18:58:41 +03:00
parent 8543c4e241
commit 21a768edd5
505 changed files with 19998 additions and 93 deletions
+4
View File
@@ -0,0 +1,4 @@
keystore.properties
release.keystore
*.keystore
build
@@ -0,0 +1,37 @@
plugins {
alias(libs.plugins.convention.android.app)
alias(libs.plugins.convention.compose)
alias(libs.plugins.convention.koin)
alias(libs.plugins.convention.ktor.client)
}
android {
buildFeatures { buildConfig = true }
namespace = "ru.shadowsparky.videobox.multiplatform"
defaultConfig {
applicationId = namespace
minSdk = libs.versions.proj.min.get().toInt()
targetSdk = libs.versions.proj.target.get().toInt()
versionCode = 35
versionName = "1.18"
}
}
dependencies {
implementation(libs.activity.compose)
implementation(libs.material)
implementation(project(":apps:vbox:client:shared"))
implementation(project(":apps:vbox:common"))
implementation(project(":libs:base"))
implementation(project(":libs:ui"))
implementation(project(":libs:http-client"))
implementation(project(":libs:video-player"))
debugImplementation(libs.ui.tooling)
compileOnly(libs.ui.tooling.preview.android)
testImplementation(libs.koin.test)
testImplementation(libs.mockk)
testImplementation(kotlin("test-junit"))
androidTestImplementation(libs.junit)
androidTestImplementation(libs.runner)
}
+41
View File
@@ -0,0 +1,41 @@
-dontwarn org.bouncycastle.jsse.BCSSLParameters
-dontwarn org.bouncycastle.jsse.BCSSLSocket
-dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
-dontwarn org.conscrypt.Conscrypt$Version
-dontwarn org.conscrypt.Conscrypt
-dontwarn org.conscrypt.ConscryptHostnameVerifier
-dontwarn org.openjsse.javax.net.ssl.SSLParameters
-dontwarn org.openjsse.javax.net.ssl.SSLSocket
-dontwarn org.openjsse.net.ssl.OpenJSSE
-dontwarn org.slf4j.impl.StaticLoggerBinder
-dontwarn java.lang.management.ManagementFactory
-dontwarn java.lang.management.RuntimeMXBean
-dontwarn kotlinx.atomicfu.**
-dontwarn io.netty.**
-dontwarn com.typesafe.**
-dontwarn org.slf4j.**
-dontwarn com.sun.nio.file.SensitivityWatchEventModifier
-dontwarn org.xmlpull.v1.**
-dontwarn javax.xml.namespace.**
-dontwarn androidx.test.platform.app.InstrumentationRegistry
-dontwarn androidx.window.extensions.WindowExtensions
-dontwarn androidx.window.extensions.WindowExtensionsProvider
-dontwarn androidx.window.extensions.area.ExtensionWindowAreaPresentation
-dontwarn androidx.window.extensions.core.util.function.Consumer
-dontwarn androidx.window.extensions.core.util.function.Function
-dontwarn androidx.window.extensions.core.util.function.Predicate
-dontwarn androidx.window.extensions.layout.DisplayFeature
-dontwarn androidx.window.extensions.layout.FoldingFeature
-dontwarn androidx.window.extensions.layout.WindowLayoutComponent
-dontwarn androidx.window.extensions.layout.WindowLayoutInfo
-dontwarn androidx.window.sidecar.SidecarDeviceState
-dontwarn androidx.window.sidecar.SidecarDisplayFeature
-dontwarn androidx.window.sidecar.SidecarInterface$SidecarCallback
-dontwarn androidx.window.sidecar.SidecarInterface
-dontwarn androidx.window.sidecar.SidecarProvider
-dontwarn androidx.window.sidecar.SidecarWindowLayoutInfo
-keep class ru.shadowsparky.vbox.shared.domain.RemoteEvent { *; }
-keep class ru.shadowsparky.vbox.shared.domain.RemoteEvent$OnSaved { *; }
-keep class ru.shadowsparky.vbox.shared.domain.RemoteEvent$OnRecent { *; }
-keep class ru.shadowsparky.vbox.shared.domain.RemoteEvent$OnSearch { *; }
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-feature
android:name="android.software.leanback"
android:required="false" />
<uses-feature
android:name="android.hardware.touchscreen"
android:required="false" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".App"
android:allowBackup="true"
android:banner="@mipmap/ic_banner"
android:enableOnBackInvokedCallback="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Videobox"
tools:ignore="UnusedAttribute">
<activity
android:name=".MainActivity"
android:configChanges="screenSize|screenLayout|orientation|smallestScreenSize"
android:exported="true"
android:supportsPictureInPicture="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="ru.shadowsparky.vbox.action.OPEN_SAVED_MOVIES" />
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="https"
android:host="box.shadowsparky.ru" />
</intent-filter>
<meta-data
android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" />
</activity>
</application>
</manifest>
Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

@@ -0,0 +1,34 @@
package ru.shadowsparky.videobox.multiplatform
import android.app.Application
import android.os.StrictMode
import com.google.android.material.color.DynamicColors
import org.koin.android.ext.koin.androidContext
import org.koin.core.annotation.KoinApplication
import org.koin.core.annotation.Module
import org.koin.core.annotation.Single
import org.koin.plugin.module.dsl.startKoin
import ru.shadowsparky.domain.AppConfig
@KoinApplication
object VBox
class App : Application() {
override fun onCreate() {
super.onCreate()
DynamicColors.applyToActivitiesIfAvailable(this)
startKoin<VBox> { androidContext(this@App) }
if (BuildConfig.DEBUG) StrictMode.enableDefaults()
}
}
@Module
class AppModule {
@Single
fun provideAppConfig(): AppConfig {
return object : AppConfig {
override fun isDebug(): Boolean = BuildConfig.DEBUG
}
}
}
@@ -0,0 +1,101 @@
package ru.shadowsparky.videobox.multiplatform
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.lifecycle.lifecycleScope
import com.arkivanov.decompose.retainedComponent
import kotlinx.coroutines.launch
import ru.shadowsparky.koin
import ru.shadowsparky.vbox.shared.presentation.Nav
import ru.shadowsparky.vbox.shared.presentation.RootComponent
import ru.shadowsparky.vbox.shared.presentation.RootFactory
import ru.shadowsparky.vbox.shared.presentation.UrlRoutes
import ru.shadowsparky.vbox.shared.presentation.nav.Route
class MainActivity : ComponentActivity() {
private lateinit var root: RootComponent
override fun onCreate(savedInstanceState: Bundle?) {
edgeToEdge(false)
super.onCreate(savedInstanceState)
root = retainedComponent { componentContext ->
val routeFromIntent = obtainRouteOrNull(intent?.data)
val route = if (routeFromIntent == null) {
listOf(Route.default)
} else {
listOf(Route.default, routeFromIntent)
}
koin.get<RootFactory>().create(componentContext, route)
}
if (openSavedIfNeeded(intent) || openUrlIfNeeded(intent.data)) {
intent = Intent(intent)
.setData(null)
.setAction(null)
}
setContent {
Nav(rootComponent = root)
edgeToEdge(isSystemInDarkTheme())
}
lifecycleScope.launch {
root.onFinish.collect {
finishAndRemoveTask()
}
}
}
private fun edgeToEdge(isDarkTheme: Boolean) {
enableEdgeToEdge(
statusBarStyle = SystemBarStyle.auto(
Color.TRANSPARENT,
Color.TRANSPARENT,
{ isDarkTheme }
),
navigationBarStyle = SystemBarStyle.light(
Color.TRANSPARENT,
Color.TRANSPARENT
)
)
}
private fun openSavedIfNeeded(intent: Intent?): Boolean {
if (intent?.action == "ru.shadowsparky.vbox.action.OPEN_SAVED_MOVIES") {
root.nav(Route.Bookmark(), true)
return true
}
return false
}
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
if (!openSavedIfNeeded(intent)) {
val uri = intent.data ?: return
val route = obtainRouteOrNull(uri)
if (route != null) {
root.nav(route, true)
} else {
openUrlIfNeeded(uri)
}
}
}
private fun openUrlIfNeeded(uri: Uri?): Boolean {
uri ?: return false
if (uri.path?.startsWith("/video/") == true) {
root.nav(Route.Overview.Player(url = uri.toString()))
return true
}
return false
}
private fun obtainRouteOrNull(uri: Uri?): Route? {
uri ?: return null
return UrlRoutes.parseUrl(uri.toString(), basePath = "/")
}
}
@@ -0,0 +1,12 @@
package ru.shadowsparky.videobox.multiplatform.preview
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import ru.shadowsparky.ui.components.TextPreference
@Preview
@Composable
fun SwitchPreferencePreview() {
TextPreference(Modifier, "WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW", "summary")
}
@@ -0,0 +1,64 @@
@file:OptIn(ExperimentalMaterial3Api::class)
package ru.shadowsparky.videobox.multiplatform.preview
import android.annotation.SuppressLint
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import ru.shadowsparky.vbox.shared.presentation.search.AppBarWithSearch
import ru.shadowsparky.vbox.shared.presentation.search.OldSearchInfo
import ru.shadowsparky.vbox.shared.presentation.search.SearchAppBar
@Preview
@Composable
fun AppBarWithSearchPreview() {
AppBarWithSearch(
title = "TitleWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW",
isDefault = false,
onBack = {}
)
}
@Preview
@Composable
fun AppBarWithSearchWithBackPreview() {
AppBarWithSearch(
title = "TitleWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW",
onBack = {}
)
}
@SuppressLint("RememberInComposition")
@Preview
@Composable
fun InternalAppBarPreview() {
SearchAppBar(
TextFieldValue(""),
FocusRequester(),
{},
{}
)
}
@Preview(showBackground = true)
@Composable
fun SearchInfoPreview() {
OldSearchInfo(
modifier = Modifier,
info = listOf(
"Почему дора дура?",
"клиинка",
"симпсоны",
"WWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWWW"
),
PaddingValues(0.dp),
onClick = {},
onDeleteRequested = {}
)
}
@@ -0,0 +1,19 @@
package ru.shadowsparky.videobox.multiplatform.preview
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import ru.shadowsparky.vbox.shared.domain.model.Season
import ru.shadowsparky.vbox.shared.presentation.overview.season.SeasonsInfo
@Preview
@Composable
fun SeasonsInfoPreview() {
SeasonsInfo(
paddingValues = PaddingValues(0.dp),
seasons = listOf(Season(listOf(), 23, "Lalalal"))
) {
}
}
@@ -0,0 +1,20 @@
package ru.shadowsparky.videobox.multiplatform.preview
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import ru.shadowsparky.vbox.shared.presentation.list.ErrorAndRetry
import ru.shadowsparky.vbox.shared.presentation.list.Loading
@Preview
@Composable
fun ErrorPreview() {
ErrorAndRetry(
errorMsg = "Неизвестная ошибка"
) {}
}
@Preview
@Composable
fun LoadingPreview() {
Loading()
}
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14zM7.33,12.5l2.14,-1.53 2.14,1.53 -0.83,-2.46 2.15,-1.5h-2.62L9.47,6l-0.84,2.54L6,8.54l2.14,1.49z" />
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6zM20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM12,14.5v-9l6,4.5 -6,4.5z" />
</vector>
@@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<group
android:scaleX="0.38866666"
android:scaleY="0.38866666"
android:translateX="7.336"
android:translateY="7.336">
<path
android:fillColor="@android:color/white"
android:pathData="M4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6zM20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM12,14.5v-9l6,4.5 -6,4.5z" />
</group>
</vector>
@@ -0,0 +1,16 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:tint="#000000"
android:viewportWidth="24"
android:viewportHeight="24">
<group
android:scaleX="0.38866666"
android:scaleY="0.38866666"
android:translateX="7.336"
android:translateY="7.336">
<path
android:fillColor="@android:color/white"
android:pathData="M4,6L2,6v14c0,1.1 0.9,2 2,2h14v-2L4,20L4,6zM20,2L8,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM12,14.5v-9l6,4.5 -6,4.5z" />
</group>
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,6.5c3.79,0 7.17,2.13 8.82,5.5 -1.65,3.37 -5.02,5.5 -8.82,5.5S4.83,15.37 3.18,12C4.83,8.63 8.21,6.5 12,6.5m0,-2C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,9.5c1.38,0 2.5,1.12 2.5,2.5s-1.12,2.5 -2.5,2.5 -2.5,-1.12 -2.5,-2.5 1.12,-2.5 2.5,-2.5m0,-2c-2.48,0 -4.5,2.02 -4.5,4.5s2.02,4.5 4.5,4.5 4.5,-2.02 4.5,-4.5 -2.02,-4.5 -4.5,-4.5z" />
</vector>
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_fragment_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".presentation.MainActivity">
</androidx.fragment.app.FragmentContainerView>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
</adaptive-icon>
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_monochrome" />
</adaptive-icon>
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_banner_background">#FFFFFF</color>
</resources>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>
@@ -0,0 +1,5 @@
<resources>
<string name="app_name">Videobox</string>
<string name="saved_movies">Сохранённые фильмы</string>
<string name="saved_movies_long">Просмотр списка ранее сохранённых фильмов</string>
</resources>
@@ -0,0 +1,6 @@
<resources>
<style name="BaseTheme" parent="@style/Theme.Material3.DynamicColors.DayNight.NoActionBar" />
<style name="Theme.Videobox" parent="BaseTheme" />
</resources>
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts xmlns:android="http://schemas.android.com/apk/res/android">
<shortcut
android:enabled="true"
android:icon="@drawable/baseline_saved_search_24"
android:shortcutId="saved_movies"
android:shortcutLongLabel="@string/saved_movies_long"
android:shortcutShortLabel="@string/saved_movies">
<intent
android:action="ru.shadowsparky.vbox.action.OPEN_SAVED_MOVIES"
android:targetClass="ru.shadowsparky.videobox.multiplatform.MainActivity"
android:targetPackage="ru.shadowsparky.videobox.multiplatform" />
</shortcut>
</shortcuts>
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<shortcuts></shortcuts>