Compare commits
10 Commits
master
...
jetpack/to
Author | SHA1 | Date | |
---|---|---|---|
|
cbbc15d9fb | ||
|
10836f538d | ||
|
012fdd5b8e | ||
|
71a6b9a31f | ||
|
ac9004fe36 | ||
|
b3d8347200 | ||
|
b27bfbd01d | ||
|
8004d14e64 | ||
|
a530f4c059 | ||
|
e5cbedb4ef |
@ -8,9 +8,9 @@ android {
|
||||
applicationId "org.libre.agosto.p2play"
|
||||
compileSdk 35
|
||||
minSdkVersion 26
|
||||
targetSdkVersion 32
|
||||
versionCode 13
|
||||
versionName "0.8.2"
|
||||
targetSdkVersion 33
|
||||
versionCode 15
|
||||
versionName "0.8.4"
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
}
|
||||
@ -22,6 +22,7 @@ android {
|
||||
}
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
compose true
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_18
|
||||
@ -35,19 +36,29 @@ android {
|
||||
kotlinOptions {
|
||||
jvmTarget = '18'
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion '1.5.4'
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation 'com.squareup.picasso:picasso:2.71828'
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||
implementation 'com.google.android.material:material:1.12.0'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.1'
|
||||
implementation 'androidx.activity:activity:1.8.0'
|
||||
implementation 'androidx.activity:activity-ktx:1.10.0'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.8.7'
|
||||
implementation 'androidx.activity:activity-compose:1.10.0'
|
||||
implementation platform('androidx.compose:compose-bom:2025.01.01')
|
||||
implementation 'androidx.compose.ui:ui'
|
||||
implementation 'androidx.compose.ui:ui-graphics'
|
||||
implementation 'androidx.compose.ui:ui-tooling-preview'
|
||||
implementation 'androidx.navigation:navigation-compose:2.8.6'
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test:runner:1.5.2'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
@ -56,5 +67,20 @@ dependencies {
|
||||
implementation 'androidx.media3:media3-ui:1.3.1'
|
||||
implementation 'androidx.media3:media3-exoplayer-hls:1.3.1'
|
||||
implementation "androidx.media3:media3-session:1.3.1"
|
||||
implementation 'com.google.code.gson:gson:2.8.8'
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
implementation "androidx.compose.material3:material3:1.3.1"
|
||||
androidTestImplementation platform('androidx.compose:compose-bom:2025.01.01')
|
||||
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
|
||||
debugImplementation 'androidx.compose.ui:ui-tooling'
|
||||
debugImplementation 'androidx.compose.ui:ui-test-manifest'
|
||||
implementation "androidx.compose.runtime:runtime"
|
||||
implementation "androidx.compose.ui:ui"
|
||||
implementation "androidx.compose.foundation:foundation"
|
||||
implementation "androidx.compose.foundation:foundation-layout"
|
||||
implementation "androidx.compose.material3:material3"
|
||||
implementation "androidx.compose.runtime:runtime-livedata"
|
||||
implementation "androidx.compose.ui:ui-tooling"
|
||||
implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0"
|
||||
implementation "io.coil-kt.coil3:coil-compose:3.0.4"
|
||||
implementation "io.coil-kt.coil3:coil-network-okhttp:3.0.4"
|
||||
}
|
||||
|
@ -13,7 +13,10 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.P2play">
|
||||
|
||||
<activity
|
||||
android:name=".activities.MainActivity"
|
||||
android:exported="false"
|
||||
android:theme="@style/Theme.P2play" />
|
||||
<activity
|
||||
android:name=".AccountActivity"
|
||||
android:exported="false"
|
||||
@ -65,6 +68,11 @@
|
||||
android:name=".AboutActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".activities.HostActivityV2"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.P2play" />
|
||||
|
||||
<service
|
||||
android:name=".services.PlaybackService"
|
||||
android:exported="true"
|
||||
|
@ -66,11 +66,6 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
binding.content.mini.miniPlayerAuthor.setOnClickListener { this.resumeVideo() }
|
||||
// binding.content.mini.setOnClickListener { this.resumeVideo() }
|
||||
binding.content.mini.miniPlayPause.setOnClickListener { this.playPausePlayer() }
|
||||
|
||||
Handler().postDelayed({
|
||||
// Title for nav_bar
|
||||
binding.navView.getHeaderView(0).findViewById<TextView>(R.id.side_emailTxt).text = getString(R.string.nav_header_subtitle) + " " + this.packageManager.getPackageInfo(this.packageName, 0).versionName
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
// Generic function for set data to RecyclerView
|
||||
@ -374,10 +369,10 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
}
|
||||
|
||||
private fun setSideData() {
|
||||
val headerView = binding.navView.getHeaderView(0)
|
||||
if (ManagerSingleton.user.status == 1) {
|
||||
binding.navView.menu.findItem(R.id.ml).isVisible = true
|
||||
|
||||
val headerView = binding.navView.getHeaderView(0)
|
||||
headerView.findViewById<TextView>(R.id.side_usernameTxt).text = ManagerSingleton.user.username
|
||||
headerView.findViewById<TextView>(R.id.side_emailTxt).text = ManagerSingleton.user.email
|
||||
if (ManagerSingleton.user.avatar != "" && headerView.findViewById<ImageView>(R.id.side_imageView) != null) {
|
||||
@ -394,6 +389,7 @@ class MainActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelecte
|
||||
}
|
||||
} else {
|
||||
binding.navView.menu.findItem(R.id.ml).isVisible = false
|
||||
headerView.findViewById<TextView>(R.id.side_emailTxt).text = getString(R.string.nav_header_subtitle) + " " + this.packageManager.getPackageInfo(this.packageName, 0).versionName
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ object ManagerSingleton {
|
||||
var videosCount: Int = 0
|
||||
lateinit var settings: SharedPreferences
|
||||
lateinit var db: Database
|
||||
|
||||
fun toast(text: String?, context: Context) {
|
||||
android.widget.Toast.makeText(context, text, android.widget.Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
@ -21,6 +22,8 @@ object ManagerSingleton {
|
||||
db.logout()
|
||||
user = UserModel()
|
||||
token = TokenModel()
|
||||
|
||||
// TODO: Close the session in the user instance
|
||||
}
|
||||
|
||||
fun reloadSettings() {
|
||||
@ -36,4 +39,8 @@ object ManagerSingleton {
|
||||
nfsw = settings.getBoolean("show_nsfw", false)
|
||||
videosCount = settings.getString("videos_count", "15")!!.toInt()
|
||||
}
|
||||
|
||||
fun isLogged (): Boolean {
|
||||
return token.token !== ""
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import android.net.Uri
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import android.webkit.WebChromeClient
|
||||
@ -139,7 +140,10 @@ class ReproductorActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
AsyncTask.execute {
|
||||
videoPlayback = this.clientVideo.getVideo(this.video.uuid)
|
||||
videoPlayback = if (ManagerSingleton.token.status == 1)
|
||||
this.clientVideo.getVideo(this.video.uuid, ManagerSingleton.token.token)
|
||||
else
|
||||
this.clientVideo.getVideo(this.video.uuid)
|
||||
// TODO: Make this configurable
|
||||
// val bufferSize = 1024 * 1024 // 1mb
|
||||
// val allocator = DefaultAllocator(true, bufferSize)
|
||||
|
@ -2,13 +2,10 @@ package org.libre.agosto.p2play
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.AsyncTask
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.preference.PreferenceManager
|
||||
import org.libre.agosto.p2play.activities.MainActivity
|
||||
import org.libre.agosto.p2play.ajax.Auth
|
||||
import org.libre.agosto.p2play.helpers.TaskManager
|
||||
import org.libre.agosto.p2play.models.TokenModel
|
||||
@ -46,7 +43,6 @@ class SplashActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun checkUser() {
|
||||
Log.d("was", "Checked")
|
||||
try {
|
||||
val token = db.getToken()
|
||||
val user = db.getUser()
|
||||
@ -56,21 +52,24 @@ class SplashActivity : AppCompatActivity() {
|
||||
val clientSecret = settings.getString("client_secret", "")!!
|
||||
val task = TaskManager<TokenModel>()
|
||||
|
||||
task.runTask({client.refreshToken(token, clientId, clientSecret)}, {
|
||||
when (token.status.toString()) {
|
||||
"1" -> {
|
||||
db.newToken(it)
|
||||
ManagerSingleton.token = it
|
||||
ManagerSingleton.user = user
|
||||
task.runTask(
|
||||
{
|
||||
client.refreshToken(token, clientId, clientSecret)
|
||||
}, {
|
||||
when (it.status.toString()) {
|
||||
"1" -> {
|
||||
db.newToken(it)
|
||||
ManagerSingleton.token = it
|
||||
ManagerSingleton.user = user
|
||||
}
|
||||
else -> ManagerSingleton.logout()
|
||||
}
|
||||
else -> ManagerSingleton.logout()
|
||||
}
|
||||
startApp()
|
||||
})
|
||||
} else {
|
||||
ManagerSingleton.logout()
|
||||
startApp()
|
||||
}
|
||||
|
||||
startApp()
|
||||
} catch (err: Exception) {
|
||||
err.printStackTrace()
|
||||
Thread.sleep(2000)
|
||||
|
@ -0,0 +1,69 @@
|
||||
package org.libre.agosto.p2play.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.constraintlayout.compose.ChainStyle
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import org.libre.agosto.p2play.MainActivity
|
||||
import org.libre.agosto.p2play.activities.ui.theme.P2playTheme
|
||||
|
||||
class HostActivityV2: ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
|
||||
|
||||
|
||||
setContent {
|
||||
P2playTheme {
|
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||
ConstraintLayout(modifier = Modifier.padding(innerPadding).fillMaxSize()) {
|
||||
val (textLabel, emailField, passwordField) = createRefs()
|
||||
Text("Iniciar session", modifier = Modifier.constrainAs(textLabel) {
|
||||
top.linkTo(parent.top)
|
||||
start.linkTo(parent.start)
|
||||
end.linkTo(parent.end)
|
||||
bottom.linkTo(emailField.top)
|
||||
})
|
||||
TextField("", {}, label = { Text("Email") }, modifier = Modifier.padding(10.dp).constrainAs(emailField) {
|
||||
top.linkTo(textLabel.bottom)
|
||||
start.linkTo(parent.start)
|
||||
end.linkTo(parent.end)
|
||||
bottom.linkTo(passwordField.top)
|
||||
})
|
||||
TextField("", {}, label = { Text("Password") }, modifier = Modifier.constrainAs(passwordField) {
|
||||
top.linkTo(emailField.bottom)
|
||||
start.linkTo(parent.start)
|
||||
end.linkTo(parent.end)
|
||||
bottom.linkTo(parent.bottom)
|
||||
})
|
||||
|
||||
createVerticalChain(textLabel, emailField, passwordField, chainStyle = ChainStyle.Packed)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun instanceSelected () {
|
||||
}
|
||||
|
||||
private fun startApp() {
|
||||
runOnUiThread {
|
||||
val intent = Intent(this, MainActivity::class.java)
|
||||
startActivity(intent)
|
||||
this.finish()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package org.libre.agosto.p2play.activities
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import org.libre.agosto.p2play.ui.Routes
|
||||
import org.libre.agosto.p2play.viewModels.VideosViewModel
|
||||
import org.libre.agosto.p2play.activities.ui.theme.P2playTheme
|
||||
import org.libre.agosto.p2play.ui.bars.MainNavigationBar
|
||||
import org.libre.agosto.p2play.ui.bars.MainTopAppBar
|
||||
import org.libre.agosto.p2play.ui.bars.SearchTopBar
|
||||
import org.libre.agosto.p2play.ui.views.SearchView
|
||||
import org.libre.agosto.p2play.ui.views.SubscriptionsView
|
||||
import org.libre.agosto.p2play.ui.views.VideosView
|
||||
import org.libre.agosto.p2play.viewModels.SubscriptionsViewModel
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
val videoViewModel: VideosViewModel = viewModel()
|
||||
val subscriptionsViewModel: SubscriptionsViewModel = viewModel()
|
||||
val navController = rememberNavController()
|
||||
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
||||
P2playTheme {
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
topBar = {
|
||||
when(currentRoute) {
|
||||
Routes.Videos.route -> MainTopAppBar(navController)
|
||||
Routes.Search.route -> SearchTopBar()
|
||||
else -> MainTopAppBar(navController)
|
||||
}
|
||||
},
|
||||
bottomBar = { MainNavigationBar(navController) }
|
||||
) { innerPadding ->
|
||||
NavHost(navController, startDestination = Routes.Videos.route, modifier = Modifier.padding(innerPadding)) {
|
||||
composable(Routes.Videos.route) { VideosView(videoViewModel) }
|
||||
composable(Routes.Subscriptions.route) { SubscriptionsView(subscriptionsViewModel) }
|
||||
composable(Routes.Search.route) { SearchView() }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,226 @@
|
||||
package org.libre.agosto.p2play.activities.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val primaryLight = Color(0xFF8A5022)
|
||||
val onPrimaryLight = Color(0xFFFFFFFF)
|
||||
val primaryContainerLight = Color(0xFFFFDCC6)
|
||||
val onPrimaryContainerLight = Color(0xFF6D390C)
|
||||
val secondaryLight = Color(0xFF755845)
|
||||
val onSecondaryLight = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLight = Color(0xFFFFDCC6)
|
||||
val onSecondaryContainerLight = Color(0xFF5B412F)
|
||||
val tertiaryLight = Color(0xFF5F6135)
|
||||
val onTertiaryLight = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLight = Color(0xFFE4E6AE)
|
||||
val onTertiaryContainerLight = Color(0xFF47491F)
|
||||
val errorLight = Color(0xFFBA1A1A)
|
||||
val onErrorLight = Color(0xFFFFFFFF)
|
||||
val errorContainerLight = Color(0xFFFFDAD6)
|
||||
val onErrorContainerLight = Color(0xFF93000A)
|
||||
val backgroundLight = Color(0xFFFFF8F5)
|
||||
val onBackgroundLight = Color(0xFF221A15)
|
||||
val surfaceLight = Color(0xFFFFF8F5)
|
||||
val onSurfaceLight = Color(0xFF221A15)
|
||||
val surfaceVariantLight = Color(0xFFF3DED2)
|
||||
val onSurfaceVariantLight = Color(0xFF52443B)
|
||||
val outlineLight = Color(0xFF84746A)
|
||||
val outlineVariantLight = Color(0xFFD7C3B7)
|
||||
val scrimLight = Color(0xFF000000)
|
||||
val inverseSurfaceLight = Color(0xFF382F29)
|
||||
val inverseOnSurfaceLight = Color(0xFFFEEDE4)
|
||||
val inversePrimaryLight = Color(0xFFFFB784)
|
||||
val surfaceDimLight = Color(0xFFE7D7CE)
|
||||
val surfaceBrightLight = Color(0xFFFFF8F5)
|
||||
val surfaceContainerLowestLight = Color(0xFFFFFFFF)
|
||||
val surfaceContainerLowLight = Color(0xFFFFF1EA)
|
||||
val surfaceContainerLight = Color(0xFFFBEBE2)
|
||||
val surfaceContainerHighLight = Color(0xFFF6E5DC)
|
||||
val surfaceContainerHighestLight = Color(0xFFF0DFD6)
|
||||
|
||||
val primaryLightMediumContrast = Color(0xFF582900)
|
||||
val onPrimaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val primaryContainerLightMediumContrast = Color(0xFF9C5E2F)
|
||||
val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val secondaryLightMediumContrast = Color(0xFF493120)
|
||||
val onSecondaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLightMediumContrast = Color(0xFF856753)
|
||||
val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryLightMediumContrast = Color(0xFF363810)
|
||||
val onTertiaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLightMediumContrast = Color(0xFF6E7042)
|
||||
val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val errorLightMediumContrast = Color(0xFF740006)
|
||||
val onErrorLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val errorContainerLightMediumContrast = Color(0xFFCF2C27)
|
||||
val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val backgroundLightMediumContrast = Color(0xFFFFF8F5)
|
||||
val onBackgroundLightMediumContrast = Color(0xFF221A15)
|
||||
val surfaceLightMediumContrast = Color(0xFFFFF8F5)
|
||||
val onSurfaceLightMediumContrast = Color(0xFF17100B)
|
||||
val surfaceVariantLightMediumContrast = Color(0xFFF3DED2)
|
||||
val onSurfaceVariantLightMediumContrast = Color(0xFF40342B)
|
||||
val outlineLightMediumContrast = Color(0xFF5E5047)
|
||||
val outlineVariantLightMediumContrast = Color(0xFF7A6A60)
|
||||
val scrimLightMediumContrast = Color(0xFF000000)
|
||||
val inverseSurfaceLightMediumContrast = Color(0xFF382F29)
|
||||
val inverseOnSurfaceLightMediumContrast = Color(0xFFFEEDE4)
|
||||
val inversePrimaryLightMediumContrast = Color(0xFFFFB784)
|
||||
val surfaceDimLightMediumContrast = Color(0xFFD3C3BB)
|
||||
val surfaceBrightLightMediumContrast = Color(0xFFFFF8F5)
|
||||
val surfaceContainerLowestLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val surfaceContainerLowLightMediumContrast = Color(0xFFFFF1EA)
|
||||
val surfaceContainerLightMediumContrast = Color(0xFFF6E5DC)
|
||||
val surfaceContainerHighLightMediumContrast = Color(0xFFEADAD1)
|
||||
val surfaceContainerHighestLightMediumContrast = Color(0xFFDECFC6)
|
||||
|
||||
val primaryLightHighContrast = Color(0xFF492100)
|
||||
val onPrimaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val primaryContainerLightHighContrast = Color(0xFF703B0E)
|
||||
val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val secondaryLightHighContrast = Color(0xFF3D2717)
|
||||
val onSecondaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLightHighContrast = Color(0xFF5E4432)
|
||||
val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryLightHighContrast = Color(0xFF2C2E07)
|
||||
val onTertiaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLightHighContrast = Color(0xFF494C22)
|
||||
val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val errorLightHighContrast = Color(0xFF600004)
|
||||
val onErrorLightHighContrast = Color(0xFFFFFFFF)
|
||||
val errorContainerLightHighContrast = Color(0xFF98000A)
|
||||
val onErrorContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val backgroundLightHighContrast = Color(0xFFFFF8F5)
|
||||
val onBackgroundLightHighContrast = Color(0xFF221A15)
|
||||
val surfaceLightHighContrast = Color(0xFFFFF8F5)
|
||||
val onSurfaceLightHighContrast = Color(0xFF000000)
|
||||
val surfaceVariantLightHighContrast = Color(0xFFF3DED2)
|
||||
val onSurfaceVariantLightHighContrast = Color(0xFF000000)
|
||||
val outlineLightHighContrast = Color(0xFF362A22)
|
||||
val outlineVariantLightHighContrast = Color(0xFF54463E)
|
||||
val scrimLightHighContrast = Color(0xFF000000)
|
||||
val inverseSurfaceLightHighContrast = Color(0xFF382F29)
|
||||
val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF)
|
||||
val inversePrimaryLightHighContrast = Color(0xFFFFB784)
|
||||
val surfaceDimLightHighContrast = Color(0xFFC5B6AD)
|
||||
val surfaceBrightLightHighContrast = Color(0xFFFFF8F5)
|
||||
val surfaceContainerLowestLightHighContrast = Color(0xFFFFFFFF)
|
||||
val surfaceContainerLowLightHighContrast = Color(0xFFFEEDE4)
|
||||
val surfaceContainerLightHighContrast = Color(0xFFF0DFD6)
|
||||
val surfaceContainerHighLightHighContrast = Color(0xFFE1D1C9)
|
||||
val surfaceContainerHighestLightHighContrast = Color(0xFFD3C3BB)
|
||||
|
||||
val primaryDark = Color(0xFFFFB784)
|
||||
val onPrimaryDark = Color(0xFF4F2500)
|
||||
val primaryContainerDark = Color(0xFF6D390C)
|
||||
val onPrimaryContainerDark = Color(0xFFFFDCC6)
|
||||
val secondaryDark = Color(0xFFE4BFA8)
|
||||
val onSecondaryDark = Color(0xFF422B1B)
|
||||
val secondaryContainerDark = Color(0xFF5B412F)
|
||||
val onSecondaryContainerDark = Color(0xFFFFDCC6)
|
||||
val tertiaryDark = Color(0xFFC8CA94)
|
||||
val onTertiaryDark = Color(0xFF30330B)
|
||||
val tertiaryContainerDark = Color(0xFF47491F)
|
||||
val onTertiaryContainerDark = Color(0xFFE4E6AE)
|
||||
val errorDark = Color(0xFFFFB4AB)
|
||||
val onErrorDark = Color(0xFF690005)
|
||||
val errorContainerDark = Color(0xFF93000A)
|
||||
val onErrorContainerDark = Color(0xFFFFDAD6)
|
||||
val backgroundDark = Color(0xFF19120D)
|
||||
val onBackgroundDark = Color(0xFFF0DFD6)
|
||||
val surfaceDark = Color(0xFF19120D)
|
||||
val onSurfaceDark = Color(0xFFF0DFD6)
|
||||
val surfaceVariantDark = Color(0xFF52443B)
|
||||
val onSurfaceVariantDark = Color(0xFFD7C3B7)
|
||||
val outlineDark = Color(0xFF9F8D83)
|
||||
val outlineVariantDark = Color(0xFF52443B)
|
||||
val scrimDark = Color(0xFF000000)
|
||||
val inverseSurfaceDark = Color(0xFFF0DFD6)
|
||||
val inverseOnSurfaceDark = Color(0xFF382F29)
|
||||
val inversePrimaryDark = Color(0xFF8A5022)
|
||||
val surfaceDimDark = Color(0xFF19120D)
|
||||
val surfaceBrightDark = Color(0xFF413731)
|
||||
val surfaceContainerLowestDark = Color(0xFF140D08)
|
||||
val surfaceContainerLowDark = Color(0xFF221A15)
|
||||
val surfaceContainerDark = Color(0xFF261E18)
|
||||
val surfaceContainerHighDark = Color(0xFF312822)
|
||||
val surfaceContainerHighestDark = Color(0xFF3C332D)
|
||||
|
||||
val primaryDarkMediumContrast = Color(0xFFFFD4B8)
|
||||
val onPrimaryDarkMediumContrast = Color(0xFF3F1C00)
|
||||
val primaryContainerDarkMediumContrast = Color(0xFFC5814F)
|
||||
val onPrimaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val secondaryDarkMediumContrast = Color(0xFFFBD5BC)
|
||||
val onSecondaryDarkMediumContrast = Color(0xFF362111)
|
||||
val secondaryContainerDarkMediumContrast = Color(0xFFAB8A75)
|
||||
val onSecondaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val tertiaryDarkMediumContrast = Color(0xFFDEE0A8)
|
||||
val onTertiaryDarkMediumContrast = Color(0xFF262802)
|
||||
val tertiaryContainerDarkMediumContrast = Color(0xFF929462)
|
||||
val onTertiaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val errorDarkMediumContrast = Color(0xFFFFD2CC)
|
||||
val onErrorDarkMediumContrast = Color(0xFF540003)
|
||||
val errorContainerDarkMediumContrast = Color(0xFFFF5449)
|
||||
val onErrorContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val backgroundDarkMediumContrast = Color(0xFF19120D)
|
||||
val onBackgroundDarkMediumContrast = Color(0xFFF0DFD6)
|
||||
val surfaceDarkMediumContrast = Color(0xFF19120D)
|
||||
val onSurfaceDarkMediumContrast = Color(0xFFFFFFFF)
|
||||
val surfaceVariantDarkMediumContrast = Color(0xFF52443B)
|
||||
val onSurfaceVariantDarkMediumContrast = Color(0xFFEDD8CC)
|
||||
val outlineDarkMediumContrast = Color(0xFFC1AEA3)
|
||||
val outlineVariantDarkMediumContrast = Color(0xFF9E8D82)
|
||||
val scrimDarkMediumContrast = Color(0xFF000000)
|
||||
val inverseSurfaceDarkMediumContrast = Color(0xFFF0DFD6)
|
||||
val inverseOnSurfaceDarkMediumContrast = Color(0xFF312822)
|
||||
val inversePrimaryDarkMediumContrast = Color(0xFF6F3A0D)
|
||||
val surfaceDimDarkMediumContrast = Color(0xFF19120D)
|
||||
val surfaceBrightDarkMediumContrast = Color(0xFF4D423C)
|
||||
val surfaceContainerLowestDarkMediumContrast = Color(0xFF0C0603)
|
||||
val surfaceContainerLowDarkMediumContrast = Color(0xFF241C17)
|
||||
val surfaceContainerDarkMediumContrast = Color(0xFF2F2620)
|
||||
val surfaceContainerHighDarkMediumContrast = Color(0xFF3A312B)
|
||||
val surfaceContainerHighestDarkMediumContrast = Color(0xFF463C35)
|
||||
|
||||
val primaryDarkHighContrast = Color(0xFFFFECE2)
|
||||
val onPrimaryDarkHighContrast = Color(0xFF000000)
|
||||
val primaryContainerDarkHighContrast = Color(0xFFFEB17B)
|
||||
val onPrimaryContainerDarkHighContrast = Color(0xFF180700)
|
||||
val secondaryDarkHighContrast = Color(0xFFFFECE2)
|
||||
val onSecondaryDarkHighContrast = Color(0xFF000000)
|
||||
val secondaryContainerDarkHighContrast = Color(0xFFE0BBA4)
|
||||
val onSecondaryContainerDarkHighContrast = Color(0xFF170700)
|
||||
val tertiaryDarkHighContrast = Color(0xFFF2F4BB)
|
||||
val onTertiaryDarkHighContrast = Color(0xFF000000)
|
||||
val tertiaryContainerDarkHighContrast = Color(0xFFC4C690)
|
||||
val onTertiaryContainerDarkHighContrast = Color(0xFF0B0C00)
|
||||
val errorDarkHighContrast = Color(0xFFFFECE9)
|
||||
val onErrorDarkHighContrast = Color(0xFF000000)
|
||||
val errorContainerDarkHighContrast = Color(0xFFFFAEA4)
|
||||
val onErrorContainerDarkHighContrast = Color(0xFF220001)
|
||||
val backgroundDarkHighContrast = Color(0xFF19120D)
|
||||
val onBackgroundDarkHighContrast = Color(0xFFF0DFD6)
|
||||
val surfaceDarkHighContrast = Color(0xFF19120D)
|
||||
val onSurfaceDarkHighContrast = Color(0xFFFFFFFF)
|
||||
val surfaceVariantDarkHighContrast = Color(0xFF52443B)
|
||||
val onSurfaceVariantDarkHighContrast = Color(0xFFFFFFFF)
|
||||
val outlineDarkHighContrast = Color(0xFFFFECE2)
|
||||
val outlineVariantDarkHighContrast = Color(0xFFD2BFB3)
|
||||
val scrimDarkHighContrast = Color(0xFF000000)
|
||||
val inverseSurfaceDarkHighContrast = Color(0xFFF0DFD6)
|
||||
val inverseOnSurfaceDarkHighContrast = Color(0xFF000000)
|
||||
val inversePrimaryDarkHighContrast = Color(0xFF6F3A0D)
|
||||
val surfaceDimDarkHighContrast = Color(0xFF19120D)
|
||||
val surfaceBrightDarkHighContrast = Color(0xFF594E47)
|
||||
val surfaceContainerLowestDarkHighContrast = Color(0xFF000000)
|
||||
val surfaceContainerLowDarkHighContrast = Color(0xFF261E18)
|
||||
val surfaceContainerDarkHighContrast = Color(0xFF382F29)
|
||||
val surfaceContainerHighDarkHighContrast = Color(0xFF433933)
|
||||
val surfaceContainerHighestDarkHighContrast = Color(0xFF4F453E)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,278 @@
|
||||
package org.libre.agosto.p2play.activities.ui.theme
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Immutable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val lightScheme = lightColorScheme(
|
||||
primary = primaryLight,
|
||||
onPrimary = onPrimaryLight,
|
||||
primaryContainer = primaryContainerLight,
|
||||
onPrimaryContainer = onPrimaryContainerLight,
|
||||
secondary = secondaryLight,
|
||||
onSecondary = onSecondaryLight,
|
||||
secondaryContainer = secondaryContainerLight,
|
||||
onSecondaryContainer = onSecondaryContainerLight,
|
||||
tertiary = tertiaryLight,
|
||||
onTertiary = onTertiaryLight,
|
||||
tertiaryContainer = tertiaryContainerLight,
|
||||
onTertiaryContainer = onTertiaryContainerLight,
|
||||
error = errorLight,
|
||||
onError = onErrorLight,
|
||||
errorContainer = errorContainerLight,
|
||||
onErrorContainer = onErrorContainerLight,
|
||||
background = backgroundLight,
|
||||
onBackground = onBackgroundLight,
|
||||
surface = surfaceLight,
|
||||
onSurface = onSurfaceLight,
|
||||
surfaceVariant = surfaceVariantLight,
|
||||
onSurfaceVariant = onSurfaceVariantLight,
|
||||
outline = outlineLight,
|
||||
outlineVariant = outlineVariantLight,
|
||||
scrim = scrimLight,
|
||||
inverseSurface = inverseSurfaceLight,
|
||||
inverseOnSurface = inverseOnSurfaceLight,
|
||||
inversePrimary = inversePrimaryLight,
|
||||
surfaceDim = surfaceDimLight,
|
||||
surfaceBright = surfaceBrightLight,
|
||||
surfaceContainerLowest = surfaceContainerLowestLight,
|
||||
surfaceContainerLow = surfaceContainerLowLight,
|
||||
surfaceContainer = surfaceContainerLight,
|
||||
surfaceContainerHigh = surfaceContainerHighLight,
|
||||
surfaceContainerHighest = surfaceContainerHighestLight,
|
||||
)
|
||||
|
||||
private val darkScheme = darkColorScheme(
|
||||
primary = primaryDark,
|
||||
onPrimary = onPrimaryDark,
|
||||
primaryContainer = primaryContainerDark,
|
||||
onPrimaryContainer = onPrimaryContainerDark,
|
||||
secondary = secondaryDark,
|
||||
onSecondary = onSecondaryDark,
|
||||
secondaryContainer = secondaryContainerDark,
|
||||
onSecondaryContainer = onSecondaryContainerDark,
|
||||
tertiary = tertiaryDark,
|
||||
onTertiary = onTertiaryDark,
|
||||
tertiaryContainer = tertiaryContainerDark,
|
||||
onTertiaryContainer = onTertiaryContainerDark,
|
||||
error = errorDark,
|
||||
onError = onErrorDark,
|
||||
errorContainer = errorContainerDark,
|
||||
onErrorContainer = onErrorContainerDark,
|
||||
background = backgroundDark,
|
||||
onBackground = onBackgroundDark,
|
||||
surface = surfaceDark,
|
||||
onSurface = onSurfaceDark,
|
||||
surfaceVariant = surfaceVariantDark,
|
||||
onSurfaceVariant = onSurfaceVariantDark,
|
||||
outline = outlineDark,
|
||||
outlineVariant = outlineVariantDark,
|
||||
scrim = scrimDark,
|
||||
inverseSurface = inverseSurfaceDark,
|
||||
inverseOnSurface = inverseOnSurfaceDark,
|
||||
inversePrimary = inversePrimaryDark,
|
||||
surfaceDim = surfaceDimDark,
|
||||
surfaceBright = surfaceBrightDark,
|
||||
surfaceContainerLowest = surfaceContainerLowestDark,
|
||||
surfaceContainerLow = surfaceContainerLowDark,
|
||||
surfaceContainer = surfaceContainerDark,
|
||||
surfaceContainerHigh = surfaceContainerHighDark,
|
||||
surfaceContainerHighest = surfaceContainerHighestDark,
|
||||
)
|
||||
|
||||
private val mediumContrastLightColorScheme = lightColorScheme(
|
||||
primary = primaryLightMediumContrast,
|
||||
onPrimary = onPrimaryLightMediumContrast,
|
||||
primaryContainer = primaryContainerLightMediumContrast,
|
||||
onPrimaryContainer = onPrimaryContainerLightMediumContrast,
|
||||
secondary = secondaryLightMediumContrast,
|
||||
onSecondary = onSecondaryLightMediumContrast,
|
||||
secondaryContainer = secondaryContainerLightMediumContrast,
|
||||
onSecondaryContainer = onSecondaryContainerLightMediumContrast,
|
||||
tertiary = tertiaryLightMediumContrast,
|
||||
onTertiary = onTertiaryLightMediumContrast,
|
||||
tertiaryContainer = tertiaryContainerLightMediumContrast,
|
||||
onTertiaryContainer = onTertiaryContainerLightMediumContrast,
|
||||
error = errorLightMediumContrast,
|
||||
onError = onErrorLightMediumContrast,
|
||||
errorContainer = errorContainerLightMediumContrast,
|
||||
onErrorContainer = onErrorContainerLightMediumContrast,
|
||||
background = backgroundLightMediumContrast,
|
||||
onBackground = onBackgroundLightMediumContrast,
|
||||
surface = surfaceLightMediumContrast,
|
||||
onSurface = onSurfaceLightMediumContrast,
|
||||
surfaceVariant = surfaceVariantLightMediumContrast,
|
||||
onSurfaceVariant = onSurfaceVariantLightMediumContrast,
|
||||
outline = outlineLightMediumContrast,
|
||||
outlineVariant = outlineVariantLightMediumContrast,
|
||||
scrim = scrimLightMediumContrast,
|
||||
inverseSurface = inverseSurfaceLightMediumContrast,
|
||||
inverseOnSurface = inverseOnSurfaceLightMediumContrast,
|
||||
inversePrimary = inversePrimaryLightMediumContrast,
|
||||
surfaceDim = surfaceDimLightMediumContrast,
|
||||
surfaceBright = surfaceBrightLightMediumContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestLightMediumContrast,
|
||||
surfaceContainerLow = surfaceContainerLowLightMediumContrast,
|
||||
surfaceContainer = surfaceContainerLightMediumContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighLightMediumContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestLightMediumContrast,
|
||||
)
|
||||
|
||||
private val highContrastLightColorScheme = lightColorScheme(
|
||||
primary = primaryLightHighContrast,
|
||||
onPrimary = onPrimaryLightHighContrast,
|
||||
primaryContainer = primaryContainerLightHighContrast,
|
||||
onPrimaryContainer = onPrimaryContainerLightHighContrast,
|
||||
secondary = secondaryLightHighContrast,
|
||||
onSecondary = onSecondaryLightHighContrast,
|
||||
secondaryContainer = secondaryContainerLightHighContrast,
|
||||
onSecondaryContainer = onSecondaryContainerLightHighContrast,
|
||||
tertiary = tertiaryLightHighContrast,
|
||||
onTertiary = onTertiaryLightHighContrast,
|
||||
tertiaryContainer = tertiaryContainerLightHighContrast,
|
||||
onTertiaryContainer = onTertiaryContainerLightHighContrast,
|
||||
error = errorLightHighContrast,
|
||||
onError = onErrorLightHighContrast,
|
||||
errorContainer = errorContainerLightHighContrast,
|
||||
onErrorContainer = onErrorContainerLightHighContrast,
|
||||
background = backgroundLightHighContrast,
|
||||
onBackground = onBackgroundLightHighContrast,
|
||||
surface = surfaceLightHighContrast,
|
||||
onSurface = onSurfaceLightHighContrast,
|
||||
surfaceVariant = surfaceVariantLightHighContrast,
|
||||
onSurfaceVariant = onSurfaceVariantLightHighContrast,
|
||||
outline = outlineLightHighContrast,
|
||||
outlineVariant = outlineVariantLightHighContrast,
|
||||
scrim = scrimLightHighContrast,
|
||||
inverseSurface = inverseSurfaceLightHighContrast,
|
||||
inverseOnSurface = inverseOnSurfaceLightHighContrast,
|
||||
inversePrimary = inversePrimaryLightHighContrast,
|
||||
surfaceDim = surfaceDimLightHighContrast,
|
||||
surfaceBright = surfaceBrightLightHighContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestLightHighContrast,
|
||||
surfaceContainerLow = surfaceContainerLowLightHighContrast,
|
||||
surfaceContainer = surfaceContainerLightHighContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighLightHighContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestLightHighContrast,
|
||||
)
|
||||
|
||||
private val mediumContrastDarkColorScheme = darkColorScheme(
|
||||
primary = primaryDarkMediumContrast,
|
||||
onPrimary = onPrimaryDarkMediumContrast,
|
||||
primaryContainer = primaryContainerDarkMediumContrast,
|
||||
onPrimaryContainer = onPrimaryContainerDarkMediumContrast,
|
||||
secondary = secondaryDarkMediumContrast,
|
||||
onSecondary = onSecondaryDarkMediumContrast,
|
||||
secondaryContainer = secondaryContainerDarkMediumContrast,
|
||||
onSecondaryContainer = onSecondaryContainerDarkMediumContrast,
|
||||
tertiary = tertiaryDarkMediumContrast,
|
||||
onTertiary = onTertiaryDarkMediumContrast,
|
||||
tertiaryContainer = tertiaryContainerDarkMediumContrast,
|
||||
onTertiaryContainer = onTertiaryContainerDarkMediumContrast,
|
||||
error = errorDarkMediumContrast,
|
||||
onError = onErrorDarkMediumContrast,
|
||||
errorContainer = errorContainerDarkMediumContrast,
|
||||
onErrorContainer = onErrorContainerDarkMediumContrast,
|
||||
background = backgroundDarkMediumContrast,
|
||||
onBackground = onBackgroundDarkMediumContrast,
|
||||
surface = surfaceDarkMediumContrast,
|
||||
onSurface = onSurfaceDarkMediumContrast,
|
||||
surfaceVariant = surfaceVariantDarkMediumContrast,
|
||||
onSurfaceVariant = onSurfaceVariantDarkMediumContrast,
|
||||
outline = outlineDarkMediumContrast,
|
||||
outlineVariant = outlineVariantDarkMediumContrast,
|
||||
scrim = scrimDarkMediumContrast,
|
||||
inverseSurface = inverseSurfaceDarkMediumContrast,
|
||||
inverseOnSurface = inverseOnSurfaceDarkMediumContrast,
|
||||
inversePrimary = inversePrimaryDarkMediumContrast,
|
||||
surfaceDim = surfaceDimDarkMediumContrast,
|
||||
surfaceBright = surfaceBrightDarkMediumContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestDarkMediumContrast,
|
||||
surfaceContainerLow = surfaceContainerLowDarkMediumContrast,
|
||||
surfaceContainer = surfaceContainerDarkMediumContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighDarkMediumContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestDarkMediumContrast,
|
||||
)
|
||||
|
||||
private val highContrastDarkColorScheme = darkColorScheme(
|
||||
primary = primaryDarkHighContrast,
|
||||
onPrimary = onPrimaryDarkHighContrast,
|
||||
primaryContainer = primaryContainerDarkHighContrast,
|
||||
onPrimaryContainer = onPrimaryContainerDarkHighContrast,
|
||||
secondary = secondaryDarkHighContrast,
|
||||
onSecondary = onSecondaryDarkHighContrast,
|
||||
secondaryContainer = secondaryContainerDarkHighContrast,
|
||||
onSecondaryContainer = onSecondaryContainerDarkHighContrast,
|
||||
tertiary = tertiaryDarkHighContrast,
|
||||
onTertiary = onTertiaryDarkHighContrast,
|
||||
tertiaryContainer = tertiaryContainerDarkHighContrast,
|
||||
onTertiaryContainer = onTertiaryContainerDarkHighContrast,
|
||||
error = errorDarkHighContrast,
|
||||
onError = onErrorDarkHighContrast,
|
||||
errorContainer = errorContainerDarkHighContrast,
|
||||
onErrorContainer = onErrorContainerDarkHighContrast,
|
||||
background = backgroundDarkHighContrast,
|
||||
onBackground = onBackgroundDarkHighContrast,
|
||||
surface = surfaceDarkHighContrast,
|
||||
onSurface = onSurfaceDarkHighContrast,
|
||||
surfaceVariant = surfaceVariantDarkHighContrast,
|
||||
onSurfaceVariant = onSurfaceVariantDarkHighContrast,
|
||||
outline = outlineDarkHighContrast,
|
||||
outlineVariant = outlineVariantDarkHighContrast,
|
||||
scrim = scrimDarkHighContrast,
|
||||
inverseSurface = inverseSurfaceDarkHighContrast,
|
||||
inverseOnSurface = inverseOnSurfaceDarkHighContrast,
|
||||
inversePrimary = inversePrimaryDarkHighContrast,
|
||||
surfaceDim = surfaceDimDarkHighContrast,
|
||||
surfaceBright = surfaceBrightDarkHighContrast,
|
||||
surfaceContainerLowest = surfaceContainerLowestDarkHighContrast,
|
||||
surfaceContainerLow = surfaceContainerLowDarkHighContrast,
|
||||
surfaceContainer = surfaceContainerDarkHighContrast,
|
||||
surfaceContainerHigh = surfaceContainerHighDarkHighContrast,
|
||||
surfaceContainerHighest = surfaceContainerHighestDarkHighContrast,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
data class ColorFamily(
|
||||
val color: Color,
|
||||
val onColor: Color,
|
||||
val colorContainer: Color,
|
||||
val onColorContainer: Color
|
||||
)
|
||||
|
||||
val unspecified_scheme = ColorFamily(
|
||||
Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun P2playTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable() () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
// dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
// val context = LocalContext.current
|
||||
// if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
//}
|
||||
|
||||
darkTheme -> darkScheme
|
||||
else -> lightScheme
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = AppTypography,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package org.libre.agosto.p2play.activities.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
val AppTypography = Typography()
|
@ -9,7 +9,7 @@ import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
|
||||
open class Client {
|
||||
protected fun newCon(uri: String, method: String, token: String = ""): HttpURLConnection {
|
||||
protected fun newCon(uri: String, method: String, token: String? = null): HttpURLConnection {
|
||||
val url = URL("https://${ManagerSingleton.url}/api/v1/$uri")
|
||||
val con = url.openConnection() as HttpURLConnection
|
||||
|
||||
@ -17,7 +17,7 @@ open class Client {
|
||||
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded")
|
||||
con.setRequestProperty("Accept", "*/*")
|
||||
|
||||
if (token != "") {
|
||||
if (token !== null) {
|
||||
con.setRequestProperty("Authorization", "Bearer $token")
|
||||
}
|
||||
|
||||
|
@ -201,8 +201,8 @@ class Videos : Client() {
|
||||
return this.getVideos(start, "-likes")
|
||||
}
|
||||
|
||||
fun getVideo(uuid: String): VideoModel {
|
||||
val con = this.newCon("videos/$uuid", "GET")
|
||||
fun getVideo(uuid: String, token: String? = null): VideoModel {
|
||||
val con = this.newCon("videos/$uuid", "GET", token)
|
||||
val video = VideoModel()
|
||||
try {
|
||||
if (con.responseCode == 200) {
|
||||
|
@ -0,0 +1,10 @@
|
||||
package org.libre.agosto.p2play.domain.data
|
||||
|
||||
enum class VideoFilterEnum(val filter: String) {
|
||||
TRENDING("trending"),
|
||||
HOT("hot"),
|
||||
LOCAL("local"),
|
||||
RECENT("recent"),
|
||||
LIKES("likes"),
|
||||
POPULAR("popular")
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package org.libre.agosto.p2play.helpers
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
|
||||
@Composable
|
||||
fun InfiniteScrollHandler(
|
||||
lazyState: LazyListState,
|
||||
buffer: Int = 1,
|
||||
onLoadMore: () -> Unit
|
||||
) {
|
||||
LaunchedEffect(lazyState) {
|
||||
snapshotFlow { lazyState.layoutInfo.visibleItemsInfo }
|
||||
.collect { visibleItems ->
|
||||
val items = lazyState.layoutInfo.totalItemsCount
|
||||
if (visibleItems.isNotEmpty() && items > buffer && visibleItems.last().index >= items - 1) {
|
||||
onLoadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
9
app/src/main/java/org/libre/agosto/p2play/ui/Routes.kt
Normal file
9
app/src/main/java/org/libre/agosto/p2play/ui/Routes.kt
Normal file
@ -0,0 +1,9 @@
|
||||
package org.libre.agosto.p2play.ui
|
||||
|
||||
import org.libre.agosto.p2play.R
|
||||
|
||||
sealed class Routes (val route: String, val title: Int?) {
|
||||
data object Videos: Routes("videos", R.string.app_name)
|
||||
data object Subscriptions: Routes("subscriptions", R.string.title_subscriptions)
|
||||
data object Search: Routes("search", null)
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package org.libre.agosto.p2play.ui.bars
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.AccountCircle
|
||||
import androidx.compose.material.icons.filled.Home
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.NavigationBar
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import org.libre.agosto.p2play.R
|
||||
import org.libre.agosto.p2play.ui.Routes
|
||||
|
||||
@Composable
|
||||
fun MainNavigationBar (navController: NavController) {
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentDestination = navBackStackEntry?.destination
|
||||
|
||||
NavigationBar {
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.Filled.Home, "home") },
|
||||
label = { Text(text = stringResource(R.string.nav_menu_videos), fontSize = 11.sp) },
|
||||
selected = currentDestination?.route == Routes.Videos.route,
|
||||
onClick = {
|
||||
navController.navigate(Routes.Videos.route) {
|
||||
// Pop up to the start destination of the graph to
|
||||
// avoid building up a large stack of destinations
|
||||
// on the back stack as users select items
|
||||
popUpTo(navController.graph.findStartDestination().id) {
|
||||
saveState = true
|
||||
}
|
||||
// Avoid multiple copies of the same destination when
|
||||
// reselecting the same item
|
||||
launchSingleTop = true
|
||||
// Restore state when reselecting a previously selected item
|
||||
restoreState = true
|
||||
}
|
||||
}
|
||||
)
|
||||
NavigationBarItem(
|
||||
icon = { Icon(painterResource(R.drawable.ic_live_tv_black_24dp), "home") },
|
||||
label = { Text(stringResource(R.string.nav_subscriptions), fontSize = 11.sp) },
|
||||
selected = currentDestination?.route == Routes.Subscriptions.route,
|
||||
onClick = {
|
||||
navController.navigate(Routes.Subscriptions.route) {
|
||||
// Pop up to the start destination of the graph to
|
||||
// avoid building up a large stack of destinations
|
||||
// on the back stack as users select items
|
||||
popUpTo(navController.graph.findStartDestination().id) {
|
||||
saveState = true
|
||||
}
|
||||
// Avoid multiple copies of the same destination when
|
||||
// reselecting the same item
|
||||
launchSingleTop = true
|
||||
// Restore state when reselecting a previously selected item
|
||||
restoreState = true
|
||||
}
|
||||
}
|
||||
)
|
||||
// NavigationBarItem(
|
||||
// icon = { Icon(painterResource(R.drawable.ic_menu_slideshow), "home") },
|
||||
// label = { Text(stringResource(R.string.playlists), fontSize = 11.sp) },
|
||||
// selected = false,
|
||||
// onClick = {}
|
||||
// )
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.Filled.AccountCircle, "home") },
|
||||
label = { Text(stringResource(R.string.you), fontSize = 11.sp) },
|
||||
selected = false,
|
||||
onClick = {}
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
package org.libre.agosto.p2play.ui.bars
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
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.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import org.libre.agosto.p2play.AboutActivity
|
||||
import org.libre.agosto.p2play.LoginActivity
|
||||
import org.libre.agosto.p2play.ManagerSingleton
|
||||
import org.libre.agosto.p2play.R
|
||||
import org.libre.agosto.p2play.ui.Routes
|
||||
import org.libre.agosto.p2play.SettingsActivity2
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MainTopAppBar(navController: NavController, modifier: Modifier = Modifier) {
|
||||
var isMenuOpen by remember { mutableStateOf(false) }
|
||||
val context = LocalContext.current
|
||||
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentDestination = navBackStackEntry?.destination
|
||||
|
||||
val title = when (currentDestination?.route) {
|
||||
Routes.Subscriptions.route -> stringResource(Routes.Subscriptions.title!!)
|
||||
else -> stringResource(R.string.app_name)
|
||||
}
|
||||
|
||||
TopAppBar(
|
||||
{ Text(title) },
|
||||
modifier,
|
||||
actions = {
|
||||
IconButton({
|
||||
navController.navigate(Routes.Search.route) {
|
||||
popUpTo(navController.graph.findStartDestination().id) {
|
||||
saveState = true
|
||||
}
|
||||
launchSingleTop = true
|
||||
restoreState = true
|
||||
}
|
||||
}) { Icon(Icons.Default.Search, "More") }
|
||||
// IconButton({}) { Icon(Icons.Default.Notifications, "More") }
|
||||
IconButton({ isMenuOpen = true }) {
|
||||
Icon(Icons.Default.MoreVert, "More")
|
||||
DropdownMenu(isMenuOpen, { isMenuOpen = false }) {
|
||||
if (ManagerSingleton.isLogged()) {
|
||||
DropdownMenuItem({ Text(stringResource(R.string.action_logout))}, {
|
||||
isMenuOpen = false
|
||||
ManagerSingleton.logout()
|
||||
})
|
||||
} else {
|
||||
DropdownMenuItem({ Text(stringResource(R.string.action_login))}, {
|
||||
isMenuOpen = false
|
||||
val intent = Intent(context, LoginActivity::class.java)
|
||||
context.startActivity(intent)
|
||||
})
|
||||
}
|
||||
DropdownMenuItem({ Text(stringResource(R.string.action_settings))}, {
|
||||
isMenuOpen = false
|
||||
val intent = Intent(context, SettingsActivity2::class.java)
|
||||
context.startActivity(intent)
|
||||
})
|
||||
DropdownMenuItem({ Text(stringResource(R.string.nav_about))}, {
|
||||
isMenuOpen = false
|
||||
val intent = Intent(context, AboutActivity::class.java)
|
||||
context.startActivity(intent)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package org.libre.agosto.p2play.ui.bars
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Close
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.SearchBar
|
||||
import androidx.compose.material3.Text
|
||||
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
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SearchTopBar () {
|
||||
var searchText by remember { mutableStateOf("") }
|
||||
var active by remember { mutableStateOf(true) }
|
||||
|
||||
SearchBar(
|
||||
query = searchText,
|
||||
onQueryChange = { searchText = it },
|
||||
onSearch = { active = false },
|
||||
active = active,
|
||||
onActiveChange = { active = it },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
placeholder = { Text("Buscar...") },
|
||||
leadingIcon = {
|
||||
IconButton (onClick = {}) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Volver")
|
||||
}
|
||||
},
|
||||
trailingIcon = {
|
||||
if (searchText.isNotEmpty()) {
|
||||
IconButton(onClick = { searchText = "" }) {
|
||||
Icon(Icons.Default.Close, contentDescription = "Limpiar")
|
||||
}
|
||||
}
|
||||
}) {
|
||||
|
||||
if (searchText.isNotEmpty()) {
|
||||
Text("Resultados para: $searchText")
|
||||
} else {
|
||||
Text("Ingresa un término de búsqueda")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package org.libre.agosto.p2play.ui.components.molecules
|
||||
|
||||
import android.graphics.drawable.Icon
|
||||
import androidx.compose.foundation.horizontalScroll
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Check
|
||||
import androidx.compose.material3.FilterChip
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
interface ChipValues<T> {
|
||||
val text: String
|
||||
val value: T
|
||||
val icon: ImageVector?
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> ChipSelector (values: ArrayList<ChipValues<T>>, value: T? = null, modifier: Modifier = Modifier, callback: (T) -> Unit) {
|
||||
Row (modifier.horizontalScroll(rememberScrollState())) {
|
||||
Spacer(modifier.width(10.dp))
|
||||
for (v in values) {
|
||||
val isSelected = value == v.value
|
||||
FilterChip(
|
||||
selected = isSelected,
|
||||
{ callback(v.value) },
|
||||
label = { Text(v.text) },
|
||||
leadingIcon = {
|
||||
if (isSelected) {
|
||||
Icon(Icons.Default.Check, "", Modifier.height(20.dp))
|
||||
} else if (v.icon !== null) {
|
||||
Icon(v.icon!!, "", Modifier.height(20.dp))
|
||||
}
|
||||
}
|
||||
)
|
||||
Spacer(modifier.width(10.dp))
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,113 @@
|
||||
package org.libre.agosto.p2play.ui.components.organisms
|
||||
|
||||
import android.content.Intent
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import coil3.compose.AsyncImage
|
||||
import org.libre.agosto.p2play.ChannelActivity
|
||||
import org.libre.agosto.p2play.ManagerSingleton
|
||||
import org.libre.agosto.p2play.R
|
||||
import org.libre.agosto.p2play.ReproductorActivity
|
||||
import org.libre.agosto.p2play.activities.ui.theme.AppTypography
|
||||
import org.libre.agosto.p2play.helpers.mapSeconds
|
||||
import org.libre.agosto.p2play.models.VideoModel
|
||||
|
||||
@Composable
|
||||
fun VideoItem (
|
||||
videoData: VideoModel
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val userImgSource = if (videoData.userImageUrl !== "") {
|
||||
"https://${ManagerSingleton.url}${videoData.userImageUrl}"
|
||||
} else {
|
||||
R.drawable.default_avatar
|
||||
}
|
||||
|
||||
Card({
|
||||
val intent = Intent(context, ReproductorActivity::class.java)
|
||||
intent.putExtra("video", videoData)
|
||||
context.startActivity(intent)
|
||||
}, modifier = Modifier.padding(vertical = 10.dp)) {
|
||||
ConstraintLayout() {
|
||||
val (thumbailImg, durationTxt, titleTxt, infoBox, userImg) = createRefs()
|
||||
Box(modifier = Modifier.fillMaxWidth().aspectRatio(16f / 9f).constrainAs(thumbailImg) {
|
||||
top.linkTo(parent.top)
|
||||
start.linkTo(parent.start)
|
||||
end.linkTo(parent.end)
|
||||
}) {
|
||||
AsyncImage(
|
||||
"https://${ManagerSingleton.url}${videoData.thumbUrl}",
|
||||
videoData.name,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier.matchParentSize()
|
||||
)
|
||||
}
|
||||
Text(mapSeconds(videoData.duration.toInt()), color = Color.Black, modifier = Modifier.alpha(0.5F).padding(5.dp).background(Color.White).constrainAs(durationTxt) {
|
||||
end.linkTo(thumbailImg.end)
|
||||
bottom.linkTo(thumbailImg.bottom)
|
||||
})
|
||||
|
||||
Box(modifier = Modifier.constrainAs(userImg) {
|
||||
top.linkTo(thumbailImg.bottom)
|
||||
start.linkTo(parent.start)
|
||||
bottom.linkTo(parent.bottom)
|
||||
}.clickable {
|
||||
val intent = Intent(context, ChannelActivity::class.java)
|
||||
intent.putExtra("channel", videoData.getChannel())
|
||||
context.startActivity(intent)
|
||||
}) {
|
||||
AsyncImage(
|
||||
userImgSource,
|
||||
"Profile photo",
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = Modifier.padding(10.dp).size(40.dp).clip(CircleShape),
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
videoData.name,
|
||||
style = AppTypography.titleMedium,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
lineHeight = 19.sp,
|
||||
textAlign = TextAlign.Left,
|
||||
modifier = Modifier.constrainAs(titleTxt) {
|
||||
top.linkTo(thumbailImg.bottom)
|
||||
start.linkTo(userImg.end)
|
||||
}.padding(top = 12.dp, end = 70.dp)
|
||||
)
|
||||
|
||||
Row (modifier = Modifier.constrainAs(infoBox) {
|
||||
top.linkTo(titleTxt.bottom)
|
||||
start.linkTo(userImg.end)
|
||||
}.padding(bottom = 5.dp)) {
|
||||
Text(videoData.nameChannel, style = AppTypography.labelSmall)
|
||||
Text(" - ", style = AppTypography.labelSmall)
|
||||
Text("${videoData.views.toString()} ${stringResource(R.string.view_text)}", style = AppTypography.labelSmall)
|
||||
}
|
||||
|
||||
// createVerticalChain(thumbailImg, titleTxt, infoBox, chainStyle = ChainStyle.Packed)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
package org.libre.agosto.p2play.ui.lists
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import org.libre.agosto.p2play.ui.components.organisms.VideoItem
|
||||
import org.libre.agosto.p2play.models.VideoModel
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.libre.agosto.p2play.helpers.InfiniteScrollHandler
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun VideoList (videos: ArrayList<VideoModel>, header: @Composable (() -> Unit)? = null, isLoading: Boolean = false, onRefresh: (() -> Unit)? = null, onLoadMore: (() -> Unit)? = null) {
|
||||
var isRefreshing by remember { mutableStateOf(false) }
|
||||
val lazyState = rememberLazyListState()
|
||||
|
||||
PullToRefreshBox(
|
||||
isRefreshing,
|
||||
{
|
||||
if (onRefresh !== null) {
|
||||
onRefresh()
|
||||
}
|
||||
}
|
||||
) {
|
||||
LazyColumn(state = lazyState) {
|
||||
if (header !== null) {
|
||||
item(key = "header") { header() }
|
||||
}
|
||||
items(videos, key = { it.id }) {
|
||||
VideoItem(it)
|
||||
}
|
||||
if (isLoading) {
|
||||
item(key = "loading") {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 20.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (onLoadMore !== null) {
|
||||
LaunchedEffect (isLoading) {
|
||||
snapshotFlow { isLoading }
|
||||
.collect {
|
||||
if (!it) {
|
||||
isRefreshing = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InfiniteScrollHandler(lazyState, 2) {
|
||||
onLoadMore()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package org.libre.agosto.p2play.ui.views
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun SearchView (modifier: Modifier = Modifier) {
|
||||
Box(modifier = modifier.background(Color.Red).width(100.dp).height(100.dp))
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package org.libre.agosto.p2play.ui.views
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import org.libre.agosto.p2play.R
|
||||
import org.libre.agosto.p2play.ajax.Videos
|
||||
import org.libre.agosto.p2play.ui.lists.VideoList
|
||||
import org.libre.agosto.p2play.ui.components.molecules.ChipSelector
|
||||
import org.libre.agosto.p2play.ui.components.molecules.ChipValues
|
||||
import org.libre.agosto.p2play.helpers.TaskManager
|
||||
import org.libre.agosto.p2play.models.VideoModel
|
||||
import org.libre.agosto.p2play.viewModels.SubscriptionsViewModel
|
||||
import java.util.ArrayList
|
||||
|
||||
@SuppressLint("MutableCollectionMutableState")
|
||||
@Composable
|
||||
fun SubscriptionsView (subscriptionsViewModel: SubscriptionsViewModel, modifier: Modifier = Modifier) {
|
||||
val videos by subscriptionsViewModel.videos.observeAsState(initial = listOf())
|
||||
val isLoading: Boolean by subscriptionsViewModel.isLoading.observeAsState(initial = false)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (videos.isEmpty()) {
|
||||
subscriptionsViewModel.loadVideos()
|
||||
}
|
||||
}
|
||||
|
||||
Column (modifier) {
|
||||
VideoList(
|
||||
ArrayList(videos),
|
||||
isLoading = isLoading,
|
||||
onRefresh = {
|
||||
if (!isLoading) {
|
||||
subscriptionsViewModel.refresh()
|
||||
}
|
||||
},
|
||||
onLoadMore = {
|
||||
if (!isLoading) {
|
||||
subscriptionsViewModel.loadVideos()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package org.libre.agosto.p2play.ui.views
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Star
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import org.libre.agosto.p2play.R
|
||||
import org.libre.agosto.p2play.viewModels.VideosViewModel
|
||||
import org.libre.agosto.p2play.ui.lists.VideoList
|
||||
import org.libre.agosto.p2play.ui.components.molecules.ChipSelector
|
||||
import org.libre.agosto.p2play.ui.components.molecules.ChipValues
|
||||
import org.libre.agosto.p2play.domain.data.VideoFilterEnum
|
||||
import java.util.ArrayList
|
||||
|
||||
@SuppressLint("MutableCollectionMutableState")
|
||||
@Composable
|
||||
fun VideosView (videosViewModel: VideosViewModel,modifier: Modifier = Modifier) {
|
||||
val chips = arrayListOf(
|
||||
object : ChipValues<VideoFilterEnum> {
|
||||
override val text = stringResource(R.string.nav_trending)
|
||||
override val value = VideoFilterEnum.TRENDING
|
||||
override val icon = ImageVector.vectorResource(R.drawable.ic_trending_up_black_24dp)
|
||||
},
|
||||
object : ChipValues<VideoFilterEnum> {
|
||||
override val text = stringResource(R.string.nav_likes)
|
||||
override val value = VideoFilterEnum.LIKES
|
||||
override val icon = ImageVector.vectorResource(R.drawable.ic_thumb_up_black_24dp)
|
||||
},
|
||||
object : ChipValues<VideoFilterEnum> {
|
||||
override val text = stringResource(R.string.nav_popular)
|
||||
override val value = VideoFilterEnum.POPULAR
|
||||
override val icon = Icons.Filled.Star
|
||||
},
|
||||
object : ChipValues<VideoFilterEnum> {
|
||||
override val text = stringResource(R.string.nav_recent)
|
||||
override val value = VideoFilterEnum.RECENT
|
||||
override val icon = ImageVector.vectorResource(R.drawable.ic_add_circle_black_24dp)
|
||||
},
|
||||
object : ChipValues<VideoFilterEnum> {
|
||||
override val text = stringResource(R.string.nav_local)
|
||||
override val value = VideoFilterEnum.LOCAL
|
||||
override val icon = ImageVector.vectorResource(R.drawable.ic_home_black_24dp)
|
||||
}
|
||||
)
|
||||
|
||||
val videoFilter by videosViewModel.videoFilter.observeAsState(initial = VideoFilterEnum.TRENDING)
|
||||
val videos by videosViewModel.videos.observeAsState(initial = listOf())
|
||||
val isLoading: Boolean by videosViewModel.isLoading.observeAsState(initial = false)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (videos.isEmpty()) {
|
||||
videosViewModel.loadVideos()
|
||||
}
|
||||
}
|
||||
|
||||
Column (modifier) {
|
||||
VideoList(
|
||||
ArrayList(videos),
|
||||
header = {
|
||||
ChipSelector(chips, videoFilter) {
|
||||
videosViewModel.changeCategory(it)
|
||||
}
|
||||
},
|
||||
isLoading = isLoading,
|
||||
onRefresh = {
|
||||
videosViewModel.refresh()
|
||||
},
|
||||
onLoadMore = {
|
||||
if (!isLoading) {
|
||||
videosViewModel.loadVideos()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package org.libre.agosto.p2play.viewModels
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.libre.agosto.p2play.ManagerSingleton
|
||||
import org.libre.agosto.p2play.ajax.Videos
|
||||
import org.libre.agosto.p2play.models.VideoModel
|
||||
|
||||
class SubscriptionsViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
|
||||
val client = Videos()
|
||||
|
||||
private val _videos = MutableLiveData<List<VideoModel>>()
|
||||
val videos: LiveData<List<VideoModel>> = _videos
|
||||
|
||||
private val _isLoading = MutableLiveData<Boolean>()
|
||||
val isLoading: LiveData<Boolean> = _isLoading
|
||||
|
||||
fun loadVideos() {
|
||||
_isLoading.value = true
|
||||
viewModelScope.launch {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
client.videoSubscriptions(ManagerSingleton.token.token, videos.value?.size ?: 0)
|
||||
}
|
||||
val data = if (videos.value !== null)
|
||||
ArrayList(videos.value!!)
|
||||
else
|
||||
ArrayList()
|
||||
data.addAll(result)
|
||||
_videos.postValue(data)
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
_videos.value = arrayListOf()
|
||||
loadVideos()
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package org.libre.agosto.p2play.viewModels
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.libre.agosto.p2play.ajax.Videos
|
||||
import org.libre.agosto.p2play.domain.data.VideoFilterEnum
|
||||
import org.libre.agosto.p2play.models.VideoModel
|
||||
|
||||
class VideosViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
|
||||
val client = Videos()
|
||||
|
||||
private val _videos = MutableLiveData<List<VideoModel>>()
|
||||
val videos: LiveData<List<VideoModel>> = _videos
|
||||
|
||||
private val _isLoading = MutableLiveData<Boolean>()
|
||||
val isLoading: LiveData<Boolean> = _isLoading
|
||||
|
||||
private val _videoFilter = MutableLiveData<VideoFilterEnum>()
|
||||
val videoFilter: LiveData<VideoFilterEnum> = _videoFilter
|
||||
|
||||
fun loadVideos() {
|
||||
_isLoading.value = true
|
||||
viewModelScope.launch {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
getVideoResource()
|
||||
}
|
||||
val data = if (videos.value !== null)
|
||||
ArrayList(videos.value!!)
|
||||
else
|
||||
ArrayList()
|
||||
data.addAll(result)
|
||||
_videos.postValue(data)
|
||||
_isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
fun changeCategory(category: VideoFilterEnum) {
|
||||
_videoFilter.value = category
|
||||
refresh()
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
_videos.value = arrayListOf()
|
||||
loadVideos()
|
||||
}
|
||||
|
||||
private fun getVideoResource(): ArrayList<VideoModel> {
|
||||
val skip = videos.value?.size ?: 0
|
||||
return when (videoFilter.value) {
|
||||
VideoFilterEnum.TRENDING -> {
|
||||
return client.getTrendingVideos(skip)
|
||||
}
|
||||
VideoFilterEnum.POPULAR -> {
|
||||
client.getPopularVideos(skip)
|
||||
}
|
||||
VideoFilterEnum.LIKES -> {
|
||||
client.getMostLikedVideos(skip)
|
||||
}
|
||||
VideoFilterEnum.RECENT -> {
|
||||
client.getLastVideos(skip)
|
||||
}
|
||||
VideoFilterEnum.LOCAL -> {
|
||||
client.getLocalVideos(skip)
|
||||
}
|
||||
else -> {
|
||||
client.getTrendingVideos(skip)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -128,7 +128,7 @@
|
||||
<!-- Start Prompt string -->
|
||||
<string name="reportDialog">Reason to report this video:</string>
|
||||
<string name="reportDialogMsg">You reported the video</string>
|
||||
<!-- TODO: Remove or change this placeholder text -->
|
||||
<string name="hello_blank_fragment">Hello blank fragment</string>
|
||||
<!-- End Prompt strings -->
|
||||
<string name="playlists">Playlists</string>
|
||||
<string name="you">You</string>
|
||||
</resources>
|
||||
|
Loading…
x
Reference in New Issue
Block a user