[feat] Better UI 2

This commit is contained in:
acite
2025-10-01 02:04:37 +08:00
parent c5a5826321
commit 24dda0eb2c
26 changed files with 512 additions and 537 deletions

View File

@@ -3,8 +3,6 @@ package com.acitelight.aether
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import android.graphics.drawable.Icon
import android.net.http.SslCertificate.saveState
import android.os.Bundle import android.os.Bundle
import android.view.WindowManager import android.view.WindowManager
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
@@ -16,36 +14,26 @@ import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.automirrored.filled.CompareArrows import androidx.compose.material.icons.automirrored.filled.CompareArrows
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardColors
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SegmentedButtonDefaults.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue 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.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
@@ -60,22 +48,18 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument import androidx.navigation.navArgument
import com.acitelight.aether.view.ComicGridView import com.acitelight.aether.view.pages.ComicGridView
import com.acitelight.aether.view.ComicPageView import com.acitelight.aether.view.pages.ComicPageView
import com.acitelight.aether.view.ComicScreen import com.acitelight.aether.view.pages.ComicScreen
import com.acitelight.aether.view.HomeScreen import com.acitelight.aether.view.pages.HomeScreen
import com.acitelight.aether.view.MeScreen import com.acitelight.aether.view.pages.MeScreen
import com.acitelight.aether.view.TransmissionScreen import com.acitelight.aether.view.pages.TransmissionScreen
import com.acitelight.aether.view.VideoPlayer import com.acitelight.aether.view.pages.VideoPlayer
import com.acitelight.aether.view.VideoScreen import com.acitelight.aether.view.pages.VideoScreen
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
@AndroidEntryPoint @AndroidEntryPoint
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {

View File

@@ -1,171 +0,0 @@
package com.acitelight.aether.view
import android.app.Activity
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.res.Configuration
import android.media.AudioManager
import android.view.View
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import androidx.navigation.NavHostController
import com.acitelight.aether.viewModel.VideoPlayerViewModel
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.VolumeUp
import androidx.compose.material.icons.filled.Brightness4
import androidx.compose.material.icons.filled.FastForward
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.LockOpen
import androidx.compose.material.icons.filled.Share
import androidx.compose.material.icons.filled.Star
import androidx.compose.material.icons.filled.ThumbDown
import androidx.compose.material.icons.filled.ThumbUp
import androidx.compose.material3.Card
import androidx.compose.material3.CardColors
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DividerDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Shadow
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontWeight
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.TextUnit
import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.viewModelScope
import androidx.media3.common.text.Cue
import androidx.media3.common.util.UnstableApi
import coil3.ImageLoader
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import com.acitelight.aether.Global
import com.acitelight.aether.ToggleFullScreen
import com.acitelight.aether.model.KeyImage
import com.acitelight.aether.model.Video
import kotlinx.coroutines.launch
import kotlin.math.abs
import kotlin.math.exp
import kotlin.math.ln
import kotlin.math.pow
fun formatTime(ms: Long): String {
if (ms <= 0) return "00:00:00"
val totalSeconds = ms / 1000
val hours = totalSeconds / 3600
val minutes = (totalSeconds % 3600) / 60
val seconds = totalSeconds % 60
return String.format("%02d:%02d:%02d", hours, minutes, seconds)
}
fun moveBrit(db: Float, activity: Activity, videoPlayerViewModel: VideoPlayerViewModel) {
val attr = activity.window.attributes
val britUi = (videoPlayerViewModel.brit - db * 0.002f).coerceIn(0f, 1f)
videoPlayerViewModel.brit = britUi
val gamma = 2.2f
val britSystem = britUi.pow(gamma).coerceIn(0.001f, 1f)
attr.screenBrightness = britSystem
activity.window.attributes = attr
}
@Composable
fun VideoPlayer(
videoPlayerViewModel: VideoPlayerViewModel = hiltViewModel<VideoPlayerViewModel>(),
videoId: String,
navController: NavHostController
) {
val context = LocalContext.current
val activity = (context as? Activity)!!
DisposableEffect(Unit) {
onDispose {
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
}
videoPlayerViewModel.init(videoId)
activity.requestedOrientation =
if(videoPlayerViewModel.isLandscape)
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
else
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
if (videoPlayerViewModel.startPlaying) {
if (videoPlayerViewModel.isLandscape) {
VideoPlayerLandscape(videoPlayerViewModel)
} else {
VideoPlayerPortal(videoPlayerViewModel, navController)
}
}
}

View File

@@ -1,4 +1,4 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box

View File

@@ -1,4 +1,4 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth

View File

@@ -0,0 +1,129 @@
package com.acitelight.aether.view.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.ModifierLocalBeyondBoundsLayout
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.navigation.NavHostController
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import com.acitelight.aether.model.Comic
import com.acitelight.aether.view.pages.toHex
import com.acitelight.aether.viewModel.ComicScreenViewModel
@Composable
fun ComicCard(
comic: Comic,
navController: NavHostController,
comicScreenViewModel: ComicScreenViewModel
) {
Card(
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface.copy(0.65f)),
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
onClick = {
val route = "comic_grid_route/${comic.id.toHex()}"
navController.navigate(route)
}
) {
Column(
modifier = Modifier
.fillMaxWidth()
) {
Box(modifier = Modifier.fillMaxSize()) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(comic.getPage(0, comicScreenViewModel.apiClient))
.memoryCacheKey("${comic.id}/${0}")
.diskCacheKey("${comic.id}/${0}")
.build(),
contentDescription = null,
imageLoader = comicScreenViewModel.imageLoader!!,
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(8.dp)),
contentScale = ContentScale.Crop,
)
Box(
Modifier
.fillMaxWidth()
.height(24.dp)
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.45f)
)
)
)
.align(Alignment.BottomCenter)
)
{
Text(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(2.dp),
fontSize = 12.sp,
text = "${comic.comic.list.size} Pages",
fontWeight = FontWeight.Bold,
color = Color.White,
maxLines = 1
)
}
}
Text(
text = comic.comic.comic_name,
fontSize = 14.sp,
lineHeight = 17.sp,
fontWeight = FontWeight.Bold,
maxLines = 2,
modifier = Modifier.padding(4.dp)
)
Box(Modifier.padding(4.dp).fillMaxWidth()){
Text(
text = "Id: ${comic.id}",
fontSize = 12.sp,
lineHeight = 14.sp,
maxLines = 1,
modifier = Modifier.align(Alignment.CenterStart)
)
Text(
text = comic.comic.author,
fontSize = 12.sp,
lineHeight = 14.sp,
maxLines = 1,
modifier = Modifier.align(Alignment.CenterEnd)
)
}
}
}
}

View File

@@ -1,4 +1,4 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues

View File

@@ -1,7 +1,5 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import android.R
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -20,10 +18,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.draw.drawWithContent import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.PaintingStyle.Companion.Stroke
import androidx.compose.ui.graphics.drawOutline import androidx.compose.ui.graphics.drawOutline
import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
@@ -36,6 +32,7 @@ import coil3.compose.AsyncImage
import coil3.request.ImageRequest import coil3.request.ImageRequest
import com.acitelight.aether.model.Video import com.acitelight.aether.model.Video
import com.acitelight.aether.service.ApiClient import com.acitelight.aether.service.ApiClient
import com.acitelight.aether.view.pages.formatTime
@Composable @Composable

View File

@@ -1,4 +1,4 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -29,6 +29,7 @@ import coil3.compose.AsyncImage
import coil3.request.ImageRequest import coil3.request.ImageRequest
import com.acitelight.aether.model.Video import com.acitelight.aether.model.Video
import com.acitelight.aether.service.ApiClient import com.acitelight.aether.service.ApiClient
import com.acitelight.aether.view.pages.formatTime
@Composable @Composable

View File

@@ -1,4 +1,4 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box

View File

@@ -1,9 +1,11 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.media.AudioManager import android.media.AudioManager
import android.view.View import android.view.View
import androidx.annotation.OptIn
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideInVertically
@@ -51,11 +53,13 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.common.util.UnstableApi import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import com.acitelight.aether.view.pages.formatTime
import com.acitelight.aether.view.pages.moveBrit
import com.acitelight.aether.viewModel.VideoPlayerViewModel import com.acitelight.aether.viewModel.VideoPlayerViewModel
import kotlin.math.abs import kotlin.math.abs
@androidx.annotation.OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
@Composable @Composable
fun PortalCorePlayer(modifier: Modifier, videoPlayerViewModel: VideoPlayerViewModel, cover: Float) { fun PortalCorePlayer(modifier: Modifier, videoPlayerViewModel: VideoPlayerViewModel, cover: Float) {
val exoPlayer: ExoPlayer = videoPlayerViewModel.player!! val exoPlayer: ExoPlayer = videoPlayerViewModel.player!!
@@ -173,7 +177,7 @@ fun PortalCorePlayer(modifier: Modifier, videoPlayerViewModel: VideoPlayerViewMo
} }
) )
androidx.compose.animation.AnimatedVisibility( AnimatedVisibility(
visible = videoPlayerViewModel.isLongPressing, visible = videoPlayerViewModel.isLongPressing,
enter = slideInVertically(initialOffsetY = { fullHeight -> -fullHeight }), enter = slideInVertically(initialOffsetY = { fullHeight -> -fullHeight }),
exit = slideOutVertically(targetOffsetY = { fullHeight -> -fullHeight }), exit = slideOutVertically(targetOffsetY = { fullHeight -> -fullHeight }),
@@ -212,7 +216,7 @@ fun PortalCorePlayer(modifier: Modifier, videoPlayerViewModel: VideoPlayerViewMo
} }
} }
androidx.compose.animation.AnimatedVisibility( AnimatedVisibility(
visible = videoPlayerViewModel.draggingPurpose == 0, visible = videoPlayerViewModel.draggingPurpose == 0,
enter = fadeIn( enter = fadeIn(
initialAlpha = 0f, initialAlpha = 0f,
@@ -235,7 +239,7 @@ fun PortalCorePlayer(modifier: Modifier, videoPlayerViewModel: VideoPlayerViewMo
) )
} }
androidx.compose.animation.AnimatedVisibility( AnimatedVisibility(
visible = videoPlayerViewModel.draggingPurpose == 2, visible = videoPlayerViewModel.draggingPurpose == 2,
enter = fadeIn( enter = fadeIn(
initialAlpha = 0f, initialAlpha = 0f,
@@ -270,7 +274,7 @@ fun PortalCorePlayer(modifier: Modifier, videoPlayerViewModel: VideoPlayerViewMo
} }
} }
androidx.compose.animation.AnimatedVisibility( AnimatedVisibility(
visible = videoPlayerViewModel.draggingPurpose == 1, visible = videoPlayerViewModel.draggingPurpose == 1,
enter = fadeIn( enter = fadeIn(
initialAlpha = 0f, initialAlpha = 0f,
@@ -310,7 +314,7 @@ fun PortalCorePlayer(modifier: Modifier, videoPlayerViewModel: VideoPlayerViewMo
.background(MaterialTheme.colorScheme.primary.copy(cover)) .background(MaterialTheme.colorScheme.primary.copy(cover))
.fillMaxSize()) .fillMaxSize())
androidx.compose.animation.AnimatedVisibility( AnimatedVisibility(
visible = (!videoPlayerViewModel.planeVisibility) || videoPlayerViewModel.locked, visible = (!videoPlayerViewModel.planeVisibility) || videoPlayerViewModel.locked,
enter = fadeIn( enter = fadeIn(
initialAlpha = 0f, initialAlpha = 0f,
@@ -331,7 +335,7 @@ fun PortalCorePlayer(modifier: Modifier, videoPlayerViewModel: VideoPlayerViewMo
) )
} }
androidx.compose.animation.AnimatedVisibility( AnimatedVisibility(
visible = videoPlayerViewModel.planeVisibility && (!videoPlayerViewModel.locked), visible = videoPlayerViewModel.planeVisibility && (!videoPlayerViewModel.locked),
enter = fadeIn( enter = fadeIn(
initialAlpha = 0f, initialAlpha = 0f,

View File

@@ -1,5 +1,6 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import android.text.Layout
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -42,9 +43,9 @@ fun SubtitleOverlay(
if (raw.isEmpty()) return if (raw.isEmpty()) return
val textAlign = when (cues.firstOrNull()?.textAlignment) { val textAlign = when (cues.firstOrNull()?.textAlignment) {
android.text.Layout.Alignment.ALIGN_CENTER -> TextAlign.Center Layout.Alignment.ALIGN_CENTER -> TextAlign.Center
android.text.Layout.Alignment.ALIGN_OPPOSITE -> TextAlign.End Layout.Alignment.ALIGN_OPPOSITE -> TextAlign.End
android.text.Layout.Alignment.ALIGN_NORMAL -> TextAlign.Start Layout.Alignment.ALIGN_NORMAL -> TextAlign.Start
else -> TextAlign.Center else -> TextAlign.Center
} }

View File

@@ -0,0 +1,186 @@
package com.acitelight.aether.view.components
import android.widget.Toast
import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import com.acitelight.aether.Global.updateRelate
import com.acitelight.aether.model.Video
import com.acitelight.aether.view.pages.formatTime
import com.acitelight.aether.view.pages.toHex
import com.acitelight.aether.viewModel.VideoScreenViewModel
import kotlinx.coroutines.launch
@Composable
fun VideoCard(
videos: List<Video>,
navController: NavHostController,
videoScreenViewModel: VideoScreenViewModel
) {
val tabIndex by videoScreenViewModel.tabIndex;
val video = videos.first()
Card(
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface.copy(0.65f)),
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.combinedClickable(
onClick = {
updateRelate(
videoScreenViewModel.videoLibrary.classesMap[videoScreenViewModel.videoLibrary.classes[tabIndex]]
?: mutableStateListOf(), video
)
val vg = videos.joinToString(",") { "${it.klass}/${it.id}" }.toHex()
val route = "video_player_route/$vg"
navController.navigate(route)
},
onLongClick = {
videoScreenViewModel.viewModelScope.launch {
for(i in videos)
{
videoScreenViewModel.download(i)
}
Toast.makeText(
videoScreenViewModel.context,
"Start downloading ${video.video.group}",
Toast.LENGTH_SHORT
).show()
}
}
),
shape = RoundedCornerShape(6.dp),
) {
Column(
modifier = Modifier
.fillMaxWidth(),
) {
Box(modifier = Modifier.fillMaxSize()) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(video.getCover(videoScreenViewModel.apiClient))
.memoryCacheKey("${video.klass}/${video.id}/cover")
.diskCacheKey("${video.klass}/${video.id}/cover")
.build(),
contentDescription = null,
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(8.dp)),
contentScale = ContentScale.Fit,
imageLoader = videoScreenViewModel.imageLoader!!
)
Box(
Modifier
.fillMaxWidth()
.height(24.dp)
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.6f)
)
)
)
.align(Alignment.BottomCenter)
)
Text(
modifier = Modifier
.align(Alignment.BottomStart)
.padding(horizontal = 2.dp),
text = "${videos.size} Videos",
fontSize = 12.sp,
fontWeight = FontWeight.Bold,
lineHeight = 13.sp,
color = Color.White
)
Text(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(horizontal = 2.dp),
text = formatTime(video.video.duration),
fontSize = 12.sp,
fontWeight = FontWeight.Bold,
lineHeight = 13.sp,
color = Color.White
)
if (videos.all{ it.isLocal })
Card(
Modifier
.align(Alignment.TopStart)
.padding(5.dp)
.widthIn(max = 46.dp)
) {
Box(Modifier.fillMaxWidth())
{
Text(
modifier = Modifier.align(Alignment.Center),
text = "Local",
fontSize = 14.sp,
fontWeight = FontWeight.Bold
)
}
}
}
Text(
text = video.video.group ?: video.video.name,
fontSize = 12.sp,
fontWeight = FontWeight.Bold,
maxLines = 2,
modifier = Modifier
.padding(4.dp)
.background(Color.Transparent)
.heightIn(min = 24.dp),
lineHeight = 14.sp
)
Spacer(modifier = Modifier.weight(1f))
Box(
modifier = Modifier.padding(horizontal = 4.dp).fillMaxWidth()
) {
Text(modifier = Modifier.align(Alignment.CenterStart), text = "Class: ${video.klass}", fontSize = 10.sp, maxLines = 1)
Text(modifier = Modifier.align(Alignment.CenterEnd), text = "Id: ${
videos.take(5).joinToString(
","
) { it.id }
}", fontSize = 10.sp, maxLines = 1)
}
}
}
}

View File

@@ -1,4 +1,4 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -39,6 +39,7 @@ import coil3.request.ImageRequest
import com.acitelight.aether.Global.updateRelate import com.acitelight.aether.Global.updateRelate
import com.acitelight.aether.model.Video import com.acitelight.aether.model.Video
import com.acitelight.aether.model.VideoDownloadItemState import com.acitelight.aether.model.VideoDownloadItemState
import com.acitelight.aether.view.pages.toHex
import com.acitelight.aether.viewModel.TransmissionScreenViewModel import com.acitelight.aether.viewModel.TransmissionScreenViewModel
import com.tonyodev.fetch2.Status import com.tonyodev.fetch2.Status
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers

View File

@@ -1,8 +1,7 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@@ -18,17 +17,11 @@ import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Stop
import androidx.compose.material3.Button
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.SliderDefaults import androidx.compose.material3.SliderDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -37,7 +30,6 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.ModifierLocalBeyondBoundsLayout
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@@ -46,15 +38,11 @@ import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import coil3.request.ImageRequest import coil3.request.ImageRequest
import com.acitelight.aether.Global.updateRelate
import com.acitelight.aether.model.Video
import com.acitelight.aether.model.VideoDownloadItemState import com.acitelight.aether.model.VideoDownloadItemState
import com.acitelight.aether.viewModel.TransmissionScreenViewModel import com.acitelight.aether.viewModel.TransmissionScreenViewModel
import com.tonyodev.fetch2.Status import com.tonyodev.fetch2.Status
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import java.io.File import java.io.File
import kotlin.math.abs import kotlin.math.abs
@@ -90,8 +78,8 @@ fun VideoDownloadCardMini(
.build() .build()
Card( Card(
colors = CardDefaults.cardColors(containerColor = Color.Transparent),
shape = RoundedCornerShape(8.dp), shape = RoundedCornerShape(8.dp),
elevation = CardDefaults.cardElevation(4.dp),
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 4.dp) .padding(horizontal = 4.dp)
@@ -108,20 +96,23 @@ fun VideoDownloadCardMini(
else -> {} else -> {}
} }
}) })
.height(85.dp) .height(100.dp)
) { ) {
Row( Row(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) )
{ {
Box(Modifier Box(Modifier
.fillMaxHeight() .fillMaxHeight())
.widthIn(max = 152.dp))
{ {
AsyncImage( AsyncImage(
model = imageModel, model = imageModel,
contentDescription = null, contentDescription = null,
modifier = Modifier.fillMaxSize(), modifier = Modifier
.height(100.dp)
.clip(RoundedCornerShape(8.dp))
.widthIn(max = 150.dp)
.background(Color.Black),
contentScale = ContentScale.Crop contentScale = ContentScale.Crop
) )

View File

@@ -1,10 +1,11 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.media.AudioManager import android.media.AudioManager
import android.view.View import android.view.View
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.annotation.OptIn
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
@@ -68,12 +69,14 @@ import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView import androidx.media3.ui.PlayerView
import com.acitelight.aether.ToggleFullScreen import com.acitelight.aether.ToggleFullScreen
import com.acitelight.aether.view.pages.formatTime
import com.acitelight.aether.view.pages.moveBrit
import com.acitelight.aether.viewModel.VideoPlayerViewModel import com.acitelight.aether.viewModel.VideoPlayerViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlin.math.abs import kotlin.math.abs
@androidx.annotation.OptIn(UnstableApi::class) @OptIn(UnstableApi::class)
@Composable @Composable
fun VideoPlayerLandscape(videoPlayerViewModel: VideoPlayerViewModel) { fun VideoPlayerLandscape(videoPlayerViewModel: VideoPlayerViewModel) {
val colorScheme = MaterialTheme.colorScheme val colorScheme = MaterialTheme.colorScheme
@@ -221,7 +224,7 @@ fun VideoPlayerLandscape(videoPlayerViewModel: VideoPlayerViewModel) {
} }
androidx.compose.animation.AnimatedVisibility( AnimatedVisibility(
visible = videoPlayerViewModel.draggingPurpose == 0, visible = videoPlayerViewModel.draggingPurpose == 0,
enter = fadeIn( enter = fadeIn(
initialAlpha = 0f, initialAlpha = 0f,
@@ -242,7 +245,7 @@ fun VideoPlayerLandscape(videoPlayerViewModel: VideoPlayerViewModel) {
) )
} }
androidx.compose.animation.AnimatedVisibility( AnimatedVisibility(
visible = videoPlayerViewModel.draggingPurpose == 2, visible = videoPlayerViewModel.draggingPurpose == 2,
enter = fadeIn( enter = fadeIn(
initialAlpha = 0f, initialAlpha = 0f,
@@ -279,7 +282,7 @@ fun VideoPlayerLandscape(videoPlayerViewModel: VideoPlayerViewModel) {
} }
} }
androidx.compose.animation.AnimatedVisibility( AnimatedVisibility(
visible = videoPlayerViewModel.draggingPurpose == 1, visible = videoPlayerViewModel.draggingPurpose == 1,
enter = fadeIn( enter = fadeIn(
initialAlpha = 0f, initialAlpha = 0f,

View File

@@ -1,5 +1,6 @@
package com.acitelight.aether.view package com.acitelight.aether.view.components
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@@ -47,8 +48,9 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.acitelight.aether.Global import com.acitelight.aether.Global
import com.acitelight.aether.Global.updateRelate
import com.acitelight.aether.ToggleFullScreen import com.acitelight.aether.ToggleFullScreen
import com.acitelight.aether.view.pages.formatTime
import com.acitelight.aether.view.pages.toHex
import com.acitelight.aether.viewModel.VideoPlayerViewModel import com.acitelight.aether.viewModel.VideoPlayerViewModel

View File

@@ -1,4 +1,4 @@
package com.acitelight.aether.view package com.acitelight.aether.view.pages
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable

View File

@@ -1,5 +1,6 @@
package com.acitelight.aether.view package com.acitelight.aether.view.pages
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -46,6 +47,7 @@ import androidx.navigation.NavHostController
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import coil3.request.ImageRequest import coil3.request.ImageRequest
import com.acitelight.aether.model.BookMark import com.acitelight.aether.model.BookMark
import com.acitelight.aether.view.components.BookmarkPop
import com.acitelight.aether.viewModel.ComicPageViewModel import com.acitelight.aether.viewModel.ComicPageViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@@ -102,7 +104,7 @@ fun ComicPageView(
) )
} }
androidx.compose.animation.AnimatedVisibility( AnimatedVisibility(
visible = showPlane, visible = showPlane,
enter = slideInVertically(initialOffsetY = { fullHeight -> -fullHeight }), enter = slideInVertically(initialOffsetY = { fullHeight -> -fullHeight }),
exit = slideOutVertically(targetOffsetY = { fullHeight -> -fullHeight }), exit = slideOutVertically(targetOffsetY = { fullHeight -> -fullHeight }),
@@ -216,7 +218,7 @@ fun ComicPageView(
} }
} }
androidx.compose.animation.AnimatedVisibility( AnimatedVisibility(
visible = showPlane, visible = showPlane,
enter = slideInVertically(initialOffsetY = { fullHeight -> fullHeight }), enter = slideInVertically(initialOffsetY = { fullHeight -> fullHeight }),
exit = slideOutVertically(targetOffsetY = { fullHeight -> fullHeight }), exit = slideOutVertically(targetOffsetY = { fullHeight -> fullHeight }),

View File

@@ -1,4 +1,4 @@
package com.acitelight.aether.view package com.acitelight.aether.view.pages
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -6,7 +6,6 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
@@ -21,28 +20,22 @@ import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridS
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Card
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Placeable import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import coil3.compose.AsyncImage import com.acitelight.aether.view.components.ComicCard
import coil3.request.ImageRequest
import com.acitelight.aether.model.Comic
import com.acitelight.aether.viewModel.ComicScreenViewModel import com.acitelight.aether.viewModel.ComicScreenViewModel
@Composable @Composable
@@ -127,11 +120,18 @@ fun ComicScreen(
val colorScheme = MaterialTheme.colorScheme val colorScheme = MaterialTheme.colorScheme
Column { Column {
Text(
text = "Comic& Images",
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier
.padding(8.dp)
.align(Alignment.Start)
)
HorizontalDivider(Modifier.padding(1.dp), thickness = 1.5.dp)
VariableGrid( VariableGrid(
modifier = Modifier modifier = Modifier
.heightIn(max = 120.dp) .heightIn(max = 88.dp)
.padding(8.dp), .padding(4.dp),
rowHeight = 32.dp rowHeight = 32.dp
) )
{ {
@@ -164,13 +164,13 @@ fun ComicScreen(
} }
} }
HorizontalDivider(thickness = 1.5.dp) HorizontalDivider(Modifier.padding(1.dp), thickness = 1.5.dp)
LazyVerticalStaggeredGrid( LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Adaptive(136.dp), columns = StaggeredGridCells.Adaptive(136.dp),
contentPadding = PaddingValues(8.dp), contentPadding = PaddingValues(8.dp),
verticalItemSpacing = 8.dp, verticalItemSpacing = 8.dp,
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(4.dp),
state = state, state = state,
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
@@ -190,86 +190,3 @@ fun ComicScreen(
} }
} }
} }
@Composable
fun ComicCard(
comic: Comic,
navController: NavHostController,
comicScreenViewModel: ComicScreenViewModel
) {
Card(
shape = RoundedCornerShape(6.dp),
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight(),
onClick = {
val route = "comic_grid_route/${comic.id.toHex()}"
navController.navigate(route)
}
) {
Column(
modifier = Modifier
.fillMaxWidth()
) {
Box(modifier = Modifier.fillMaxSize()) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(comic.getPage(0, comicScreenViewModel.apiClient))
.memoryCacheKey("${comic.id}/${0}")
.diskCacheKey("${comic.id}/${0}")
.build(),
contentDescription = null,
imageLoader = comicScreenViewModel.imageLoader!!,
modifier = Modifier
.fillMaxSize(),
contentScale = ContentScale.Crop,
)
Box(
Modifier
.fillMaxWidth()
.height(24.dp)
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.45f)
)
)
)
.align(Alignment.BottomCenter)
)
{
Text(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(2.dp),
fontSize = 12.sp,
text = "${comic.comic.list.size} Pages",
fontWeight = FontWeight.Bold,
color = Color.White
)
}
}
Text(
text = comic.comic.comic_name,
fontSize = 14.sp,
fontWeight = FontWeight.Bold,
maxLines = 2,
modifier = Modifier
.padding(4.dp)
.background(Color.Transparent)
.heightIn(max = 48.dp)
)
Spacer(Modifier.height(4.dp))
Text(
text = "Id: ${comic.id}",
fontSize = 12.sp,
maxLines = 2,
modifier = Modifier
.padding(bottom = 4.dp).padding(horizontal = 4.dp)
.background(Color.Transparent)
)
}
}
}

View File

@@ -1,4 +1,4 @@
package com.acitelight.aether.view package com.acitelight.aether.view.pages
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
@@ -36,16 +36,18 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import coil3.compose.AsyncImage import coil3.compose.AsyncImage
import coil3.request.ImageRequest import coil3.request.ImageRequest
import com.acitelight.aether.Global.updateRelate import com.acitelight.aether.Global.updateRelate
import com.acitelight.aether.model.Comic import com.acitelight.aether.model.Comic
import com.acitelight.aether.view.components.MiniVideoCard
import com.acitelight.aether.viewModel.HomeScreenViewModel import com.acitelight.aether.viewModel.HomeScreenViewModel
@Composable @Composable
fun HomeScreen( fun HomeScreen(
homeScreenViewModel: HomeScreenViewModel = androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel<HomeScreenViewModel>(), homeScreenViewModel: HomeScreenViewModel = hiltViewModel<HomeScreenViewModel>(),
navController: NavHostController navController: NavHostController
) { ) {
val pagerState = rememberPagerState(initialPage = 0, pageCount = { 2 }) val pagerState = rememberPagerState(initialPage = 0, pageCount = { 2 })
@@ -54,7 +56,6 @@ fun HomeScreen(
state = pagerState, state = pagerState,
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(Color.Black)
) { p -> ) { p ->
if (p == 0) { if (p == 0) {
Column(Modifier.fillMaxHeight()) { Column(Modifier.fillMaxHeight()) {

View File

@@ -1,4 +1,4 @@
package com.acitelight.aether.view package com.acitelight.aether.view.pages
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -32,10 +32,11 @@ import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.acitelight.aether.viewModel.MeScreenViewModel import com.acitelight.aether.viewModel.MeScreenViewModel
@Composable @Composable
fun MeScreen(meScreenViewModel: MeScreenViewModel = androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel<MeScreenViewModel>()) { fun MeScreen(meScreenViewModel: MeScreenViewModel = hiltViewModel<MeScreenViewModel>()) {
var username by meScreenViewModel.username var username by meScreenViewModel.username
var privateKey by meScreenViewModel.privateKey var privateKey by meScreenViewModel.privateKey
var url by meScreenViewModel.url var url by meScreenViewModel.url

View File

@@ -1,58 +1,28 @@
package com.acitelight.aether.view package com.acitelight.aether.view.pages
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.Stop
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DividerDefaults import androidx.compose.material3.DividerDefaults
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import coil3.compose.AsyncImage
import coil3.request.ImageRequest
import com.acitelight.aether.Global.updateRelate
import com.acitelight.aether.model.VideoDownloadItemState import com.acitelight.aether.model.VideoDownloadItemState
import com.acitelight.aether.model.Video import com.acitelight.aether.view.components.BiliMiniSlider
import com.acitelight.aether.view.components.VideoDownloadCardMini
import com.acitelight.aether.viewModel.TransmissionScreenViewModel import com.acitelight.aether.viewModel.TransmissionScreenViewModel
import com.tonyodev.fetch2.Status import com.tonyodev.fetch2.Status
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json
import java.io.File
import kotlin.collections.sortedWith import kotlin.collections.sortedWith
import kotlin.math.abs
@Composable @Composable
fun TransmissionScreen( fun TransmissionScreen(
@@ -65,12 +35,16 @@ fun TransmissionScreen(
Text( Text(
text = "Video Tasks", text = "Video Tasks",
style = MaterialTheme.typography.headlineMedium, style = MaterialTheme.typography.headlineMedium,
modifier = Modifier.padding(8.dp).align(Alignment.Start) modifier = Modifier
.padding(8.dp)
.align(Alignment.Start)
) )
Text( Text(
text = "All: ${downloads.count { it.type == "main" }}", text = "All: ${downloads.count { it.type == "main" }}",
modifier = Modifier.padding(horizontal = 8.dp).align(Alignment.Start), modifier = Modifier
.padding(horizontal = 8.dp)
.align(Alignment.Start),
fontSize = 12.sp, fontSize = 12.sp,
lineHeight = 13.sp, lineHeight = 13.sp,
maxLines = 1 maxLines = 1
@@ -78,7 +52,9 @@ fun TransmissionScreen(
Text( Text(
text = "Completed: ${downloads.count { it.type == "main" && it.status == Status.COMPLETED }}", text = "Completed: ${downloads.count { it.type == "main" && it.status == Status.COMPLETED }}",
modifier = Modifier.padding(horizontal = 8.dp).align(Alignment.Start), modifier = Modifier
.padding(horizontal = 8.dp)
.align(Alignment.Start),
fontSize = 12.sp, fontSize = 12.sp,
lineHeight = 13.sp, lineHeight = 13.sp,
maxLines = 1 maxLines = 1
@@ -86,7 +62,8 @@ fun TransmissionScreen(
val downloading = downloads.filter { it.status == Status.DOWNLOADING } val downloading = downloads.filter { it.status == Status.DOWNLOADING }
BiliMiniSlider( BiliMiniSlider(
value = if (downloading.sumOf { it.totalBytes } == 0L) 1f else downloading.sumOf { it.downloadedBytes } / downloading.sumOf { it.totalBytes }.toFloat(), value = if (downloading.sumOf { it.totalBytes } == 0L) 1f else downloading.sumOf { it.downloadedBytes } / downloading.sumOf { it.totalBytes }
.toFloat(),
modifier = Modifier modifier = Modifier
.height(6.dp) .height(6.dp)
.align(Alignment.End) .align(Alignment.End)
@@ -99,11 +76,15 @@ fun TransmissionScreen(
HorizontalDivider(Modifier.padding(8.dp), 2.dp, DividerDefaults.color) HorizontalDivider(Modifier.padding(8.dp), 2.dp, DividerDefaults.color)
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth()
verticalArrangement = Arrangement.spacedBy(12.dp)
) )
{ {
items(downloads.filter { it.type == "main" }.sortedBy { it.status == Status.COMPLETED }, key = { it.id }) { item -> items(
downloads
.filter { it.type == "main" }
.sortedWith(compareBy(naturalOrder()) { it.fileName })
.sortedBy { it.status == Status.COMPLETED }, key = { it.id })
{ item ->
VideoDownloadCardMini( VideoDownloadCardMini(
navigator = navigator, navigator = navigator,
viewModel = transmissionScreenViewModel, viewModel = transmissionScreenViewModel,
@@ -139,6 +120,11 @@ fun TransmissionScreen(
)) transmissionScreenViewModel.retry(i.id) )) transmissionScreenViewModel.retry(i.id)
} }
) )
HorizontalDivider(
Modifier.padding(horizontal = 16.dp, vertical = 6.dp),
2.dp,
DividerDefaults.color
)
} }
} }
} }

View File

@@ -0,0 +1,68 @@
package com.acitelight.aether.view.pages
import android.app.Activity
import android.content.pm.ActivityInfo
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavHostController
import com.acitelight.aether.viewModel.VideoPlayerViewModel
import androidx.compose.runtime.DisposableEffect
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.acitelight.aether.view.components.VideoPlayerLandscape
import com.acitelight.aether.view.components.VideoPlayerPortal
import kotlin.math.pow
fun formatTime(ms: Long): String {
if (ms <= 0) return "00:00:00"
val totalSeconds = ms / 1000
val hours = totalSeconds / 3600
val minutes = (totalSeconds % 3600) / 60
val seconds = totalSeconds % 60
return String.format("%02d:%02d:%02d", hours, minutes, seconds)
}
fun moveBrit(db: Float, activity: Activity, videoPlayerViewModel: VideoPlayerViewModel) {
val attr = activity.window.attributes
val britUi = (videoPlayerViewModel.brit - db * 0.002f).coerceIn(0f, 1f)
videoPlayerViewModel.brit = britUi
val gamma = 2.2f
val britSystem = britUi.pow(gamma).coerceIn(0.001f, 1f)
attr.screenBrightness = britSystem
activity.window.attributes = attr
}
@Composable
fun VideoPlayer(
videoPlayerViewModel: VideoPlayerViewModel = hiltViewModel<VideoPlayerViewModel>(),
videoId: String,
navController: NavHostController
) {
val context = LocalContext.current
val activity = (context as? Activity)!!
DisposableEffect(Unit) {
onDispose {
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
}
}
videoPlayerViewModel.init(videoId)
activity.requestedOrientation =
if(videoPlayerViewModel.isLandscape)
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
else
ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
if (videoPlayerViewModel.startPlaying) {
if (videoPlayerViewModel.isLandscape) {
VideoPlayerLandscape(videoPlayerViewModel)
} else {
VideoPlayerPortal(videoPlayerViewModel, navController)
}
}
}

View File

@@ -1,4 +1,4 @@
package com.acitelight.aether.view package com.acitelight.aether.view.pages
import android.widget.Toast import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
@@ -65,6 +65,7 @@ import androidx.navigation.NavHostController
import coil3.request.ImageRequest import coil3.request.ImageRequest
import com.acitelight.aether.CardPage import com.acitelight.aether.CardPage
import com.acitelight.aether.Global.updateRelate import com.acitelight.aether.Global.updateRelate
import com.acitelight.aether.view.components.VideoCard
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.nio.charset.Charset import java.nio.charset.Charset
import kotlin.collections.sortedWith import kotlin.collections.sortedWith
@@ -116,15 +117,23 @@ fun VideoScreen(
Column( Column(
modifier = Modifier.fillMaxSize() modifier = Modifier.fillMaxSize()
) { ) {
Text(
text = "Videos",
style = MaterialTheme.typography.headlineMedium,
modifier = Modifier
.padding(horizontal = 8.dp)
.align(Alignment.Start)
)
// TopRow(videoScreenViewModel); // TopRow(videoScreenViewModel);
Row(Modifier.padding(bottom = 4.dp)) Row(Modifier.padding(bottom = 4.dp).padding(start = 8.dp))
{ {
Card( Card(
shape = RoundedCornerShape(8.dp), shape = RoundedCornerShape(8.dp),
colors = CardDefaults.cardColors(containerColor = colorScheme.primary), colors = CardDefaults.cardColors(containerColor = colorScheme.primary),
modifier = Modifier modifier = Modifier
.align(Alignment.CenterVertically) .align(Alignment.CenterVertically)
.padding(horizontal = 2.dp) .padding(horizontal = 1.dp)
.size(36.dp), .size(36.dp),
onClick = { onClick = {
menuVisibility = !menuVisibility menuVisibility = !menuVisibility
@@ -147,7 +156,7 @@ fun VideoScreen(
colors = CardDefaults.cardColors(containerColor = colorScheme.primary), colors = CardDefaults.cardColors(containerColor = colorScheme.primary),
modifier = Modifier modifier = Modifier
.align(Alignment.CenterVertically) .align(Alignment.CenterVertically)
.padding(horizontal = 2.dp) .padding(horizontal = 1.dp)
.height(36.dp), .height(36.dp),
onClick = { onClick = {
menuVisibility = !menuVisibility menuVisibility = !menuVisibility
@@ -199,15 +208,15 @@ fun VideoScreen(
} }
} }
HorizontalDivider( HorizontalDivider(
Modifier.padding(bottom = 8.dp), Modifier.padding(4.dp),
1.5.dp, 2.dp,
DividerDefaults.color DividerDefaults.color
) )
LazyVerticalStaggeredGrid( LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Adaptive(160.dp), columns = StaggeredGridCells.Adaptive(160.dp),
contentPadding = PaddingValues(8.dp), contentPadding = PaddingValues(8.dp),
verticalItemSpacing = 8.dp, verticalItemSpacing = 8.dp,
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy( horizontalArrangement = Arrangement.spacedBy(
8.dp 8.dp
), ),
state = state, state = state,
@@ -217,7 +226,7 @@ fun VideoScreen(
items = vb, items = vb,
key = { "${it.first}/${it.second}" } key = { "${it.first}/${it.second}" }
) { video -> ) { video ->
androidx.compose.foundation.layout.Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.wrapContentHeight() .wrapContentHeight()
@@ -295,140 +304,3 @@ fun CatalogueItemRow(
) )
} }
} }
@Composable
fun VideoCard(
videos: List<Video>,
navController: NavHostController,
videoScreenViewModel: VideoScreenViewModel
) {
val tabIndex by videoScreenViewModel.tabIndex;
val video = videos.first()
Card(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.combinedClickable(
onClick = {
updateRelate(
videoScreenViewModel.videoLibrary.classesMap[videoScreenViewModel.videoLibrary.classes[tabIndex]]
?: mutableStateListOf(), video
)
val vg = videos.joinToString(",") { "${it.klass}/${it.id}" }.toHex()
val route = "video_player_route/$vg"
navController.navigate(route)
},
onLongClick = {
videoScreenViewModel.viewModelScope.launch {
for(i in videos)
{
videoScreenViewModel.download(i)
}
Toast.makeText(
videoScreenViewModel.context,
"Start downloading ${video.video.group}",
Toast.LENGTH_SHORT
).show()
}
}
),
shape = RoundedCornerShape(6.dp),
) {
Column(
modifier = Modifier
.fillMaxWidth(),
) {
Box(modifier = Modifier.fillMaxSize()) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data(video.getCover(videoScreenViewModel.apiClient))
.memoryCacheKey("${video.klass}/${video.id}/cover")
.diskCacheKey("${video.klass}/${video.id}/cover")
.build(),
contentDescription = null,
modifier = Modifier
.fillMaxSize(),
contentScale = ContentScale.Fit,
imageLoader = videoScreenViewModel.imageLoader!!
)
Box(
Modifier
.fillMaxWidth()
.height(24.dp)
.background(
brush = Brush.verticalGradient(
colors = listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.6f)
)
)
)
.align(Alignment.BottomCenter)
)
Text(
modifier = Modifier
.align(Alignment.BottomStart)
.padding(horizontal = 2.dp),
text = "${videos.size} Videos",
fontSize = 12.sp,
fontWeight = FontWeight.Bold,
lineHeight = 13.sp,
color = Color.White
)
Text(
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(horizontal = 2.dp),
text = formatTime(video.video.duration),
fontSize = 12.sp,
fontWeight = FontWeight.Bold,
lineHeight = 13.sp,
color = Color.White
)
if (videos.all{ it.isLocal })
Card(
Modifier
.align(Alignment.TopStart)
.padding(5.dp)
.widthIn(max = 46.dp)
) {
Box(Modifier.fillMaxWidth())
{
Text(
modifier = Modifier.align(Alignment.Center),
text = "Local",
fontSize = 14.sp,
fontWeight = FontWeight.Bold
)
}
}
}
Text(
text = video.video.group ?: video.video.name,
fontSize = 12.sp,
fontWeight = FontWeight.Bold,
maxLines = 4,
modifier = Modifier
.padding(8.dp)
.background(Color.Transparent)
.heightIn(min = 24.dp),
lineHeight = 14.sp
)
Spacer(modifier = Modifier.weight(1f))
Row(
modifier = Modifier.padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text("Class: ", fontSize = 10.sp, maxLines = 1)
Text(video.klass, fontSize = 10.sp, maxLines = 1)
}
}
}
}

View File

@@ -15,7 +15,7 @@ import com.acitelight.aether.service.ApiClient
import com.acitelight.aether.service.FetchManager import com.acitelight.aether.service.FetchManager
import com.acitelight.aether.service.MediaManager import com.acitelight.aether.service.MediaManager
import com.acitelight.aether.service.VideoLibrary import com.acitelight.aether.service.VideoLibrary
import com.acitelight.aether.view.toHex import com.acitelight.aether.view.pages.toHex
import com.tonyodev.fetch2.Download import com.tonyodev.fetch2.Download
import com.tonyodev.fetch2.FetchListener import com.tonyodev.fetch2.FetchListener
import com.tonyodev.fetch2.Status import com.tonyodev.fetch2.Status

View File

@@ -36,8 +36,8 @@ import com.acitelight.aether.service.ApiClient
import com.acitelight.aether.service.MediaManager import com.acitelight.aether.service.MediaManager
import com.acitelight.aether.service.RecentManager import com.acitelight.aether.service.RecentManager
import com.acitelight.aether.service.VideoLibrary import com.acitelight.aether.service.VideoLibrary
import com.acitelight.aether.view.formatTime import com.acitelight.aether.view.pages.formatTime
import com.acitelight.aether.view.hexToString import com.acitelight.aether.view.pages.hexToString
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope