Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
47109ba
feat: 添加TV遥控器D-pad焦点支持
wdw89 Jun 21, 2026
f84adee
feat: TV遥控器焦点样式优化
wdw89 Jun 22, 2026
b80773a
refactor: 统一动漫源切换为监听+直接刷新模式
wdw89 Jun 25, 2026
76be33b
docs: 添加源切换统一改造spec文档
wdw89 Jun 25, 2026
b101d29
feat: 搜索页重构 & 验证码增强 & TV焦点导航优化
wdw89 Jul 5, 2026
c8e5286
fix: HomeScreen layout and focus — Anime/source button vertical layou…
wdw89 Jul 10, 2026
5f2a47c
feat: TV focus inverse-color — all buttons, dialogs, lists, sliders, …
wdw89 Jul 10, 2026
0d3adae
docs: OpenSpec artifacts for focus inverse-color change — specs, arch…
wdw89 Jul 10, 2026
5425638
chore: 移除搜索页多余的 @Preview 函数(本地调试用,原作者版本无)
wdw89 Jul 10, 2026
21f4dab
chore: remove .github .junie .mimocode .opencode from tracking, add t…
wdw89 Jul 10, 2026
9340436
chore: add .github .junie .mimocode .opencode to .gitignore
wdw89 Jul 10, 2026
f80ebf0
feat: add TV banner icon and LEANBACK_LAUNCHER support
wdw89 Jul 10, 2026
be5c99f
fix: 横屏首页站源上方空白过多
wdw89 Jul 10, 2026
3942d0e
feat(focus): fix detail page & settings page D-pad navigation
wdw89 Jul 10, 2026
92d1501
refactor: 提取共享 focus 工具函数,消除 ~65 处重复样板
wdw89 Jul 11, 2026
7bcc47b
feat(focus): v5 final — FocusRequester function-level + moveFocus for…
wdw89 Jul 11, 2026
a20ccc3
chore: delete empty home-screen-focus-fixes and slider-dpad-support c…
wdw89 Jul 11, 2026
2c04d97
feat(video-player): TV D-pad focus navigation, slider thumb animation…
wdw89 Jul 12, 2026
e5a74a6
fix: player focus bugs + gradient overlay
wdw89 Jul 12, 2026
d6769ee
fix: slider thumb sub-pixel white ring artifact
wdw89 Jul 12, 2026
cda41da
feat: focus highlight unification + TV interaction fixes
wdw89 Jul 25, 2026
bc2a4e0
fix: remove exit confirmation dialog, let Back exit app directly per …
wdw89 Jul 25, 2026
6524af4
fix: search bar status bar padding and edit/non-edit height stability
wdw89 Jul 26, 2026
03b19ce
feat: add pure background toggle (黑白背景) for flat black/white backgrounds
wdw89 Jul 26, 2026
516004f
chore: add openspec/ to gitignore, update tasks.json
wdw89 Jul 26, 2026
e6f44e7
perf: optimize TV card scroll jank with draw-phase focus border
wdw89 Jul 28, 2026
c9728c4
fix: add horizontal edge safe zone to player drag gestures
wdw89 Jul 28, 2026
4b611b2
update ignore
wdw89 Jul 28, 2026
fa60188
feat: darken player gradient overlay & show video resolution/bitrate …
wdw89 Jul 28, 2026
df2ed1a
perf: switch to draw-phase focusBorder using Modifier.composed
wdw89 Jul 28, 2026
630b5ee
feat: player multi-channel episode selection & fixes
wdw89 Aug 2, 2026
88f8d54
refactor: simplify side sheet focus state machine
wdw89 Aug 2, 2026
f4dd71d
fix: focus current channel in detail page channel selector dialog
wdw89 Aug 2, 2026
005ef36
fix: prevent speed side sheet labels from being truncated
wdw89 Aug 2, 2026
77bcdca
feat: prefix Bitrate label to player subtitle bitrate display
wdw89 Aug 2, 2026
9676eda
refactor: consolidate TV focus highlight boilerplate and fix MediaSma…
wdw89 Aug 2, 2026
bb3a92c
chore: stop tracking openspec/ (now ignored)
wdw89 Aug 2, 2026
785e467
gitignore task
wdw89 Aug 3, 2026
ec3b5de
chore: 恢复误删的 CI 工作流,本地文档移出跟踪
wdw89 Sep 14, 2026
0a0186e
ci: 移除已失效的 setup-android 步骤,恢复 CI 绿灯
wdw89 Sep 16, 2026
4dfc4db
fix(focus): 修复相关推荐首项被回收后按 ↓ 崩溃
wdw89 Sep 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/android_ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ jobs:

# Set up JDK 17
- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'

- name: Set up Android SDK
uses: android-actions/setup-android@v3
# 不再用 android-actions/setup-android:镜像已预装本项目需要的 SDK
# (ANDROID_HOME/cmdline-tools 12.0/platforms;android-36/build-tools 36.0.0)且已接受许可。
# 该 action 会因为版本与本镜像不符而自行下载固定版本的 cmdline-tools,
# 而 Google 已将这些 zip 下架 → 下载 404,整个 job 在构建前就挂掉。
# 即使在最新版 v4 上同样如此,上游 issue:android-actions/setup-android#536。

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ jobs:

# Set up JDK 17
- name: Set up JDK 17
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: 'temurin'
java-version: '17'

- name: Set up Android SDK
uses: android-actions/setup-android@v3
# 同 android_ci.yml:不用 android-actions/setup-android,
# 它下载的 cmdline-tools 已被 Google 下架(404),镜像自带的本项目需要的 SDK。

- name: Set up Gradle
uses: gradle/actions/setup-gradle@v4
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,9 @@ local.properties
release/
/.kotlin
debug/
nul
.junie
.mimocode
.opencode
openspec/
.vscode/
11 changes: 10 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

<uses-feature android:name="android.software.leanback" android:required="false" />
<uses-feature android:name="android.hardware.touchscreen" android:required="false" />

<application
android:name=".application.AnimeApplication"
android:allowBackup="true"
android:banner="@drawable/ic_banner"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
Expand All @@ -28,6 +32,10 @@

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>

<activity
Expand All @@ -37,7 +45,8 @@

<activity
android:name=".presentation.screen.captcha.CaptchaWebViewActivity"
android:exported="false" />
android:exported="false"
android:windowSoftInputMode="adjustResize" />

<provider
android:name="androidx.core.content.FileProvider"
Expand Down
4 changes: 2 additions & 2 deletions app/src/main/java/com/lanlinju/animius/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ private fun App(modifier: Modifier = Modifier) {
onNavigateToDownload = {
navController.navigate(Screen.Download)
},
onNavigateToDownloadDetail = { detailUrl, title ->
navController.navigate(Screen.DownloadDetail(detailUrl, title))
onNavigateToDownloadDetail = { detailUrl, title, sourceMode ->
navController.navigate(Screen.DownloadDetail(detailUrl, title, sourceMode))
},
onNavigateToSearch = {
navController.navigate(Screen.Search)
Expand Down
4 changes: 3 additions & 1 deletion app/src/main/java/com/lanlinju/animius/domain/model/Video.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ data class Video(
val lastPlayPosition: Long = 0L, /* 记忆播放视频位置,单位:毫秒 */
val currentEpisodeIndex: Int, /* 当前播放剧集索引,根据[episodeName]计算得出*/
val episodes: List<Episode>,
val headers: Map<String, String> = emptyMap() /* 用于配置Referer, User-Agent等*/
val headers: Map<String, String> = emptyMap(), /* 用于配置Referer, User-Agent等*/
val channels: Map<Int, List<Episode>> = emptyMap(), /* 多线路剧集 */
val channelIndex: Int = 0, /* 当前线路索引 */
)
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,16 @@ import androidx.compose.material3.MaterialTheme
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.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import com.lanlinju.animius.R
import com.lanlinju.animius.util.focus.focusedIconButtonColors
import com.lanlinju.animius.util.focus.rememberIsFocused

@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand All @@ -21,6 +28,8 @@ fun BackTopAppBar(
actions: @Composable RowScope.() -> Unit = {},
onBackClick: () -> Unit
) {
val (backFocused, focusModifier) = rememberIsFocused()

TopAppBar(
title = {
Text(
Expand All @@ -31,10 +40,16 @@ fun BackTopAppBar(
)
},
navigationIcon = {
IconButton(onClick = onBackClick) {
IconButton(
colors = focusedIconButtonColors(backFocused),
modifier = focusModifier,
onClick = onBackClick
) {
Icon(
imageVector = Icons.AutoMirrored.Rounded.ArrowBack,
contentDescription = stringResource(id = R.string.back)
contentDescription = stringResource(id = R.string.back),
tint = if (backFocused) MaterialTheme.colorScheme.onPrimary
else MaterialTheme.colorScheme.onSurface
)
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.lanlinju.animius.presentation.component

import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Delete
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

/**
* Trash-icon overlay shown on top of media cards while in delete mode.
*
* @param visible whether delete mode is active
* @param modifier sizes/positions the overlay over the card
*/
@Composable
fun DeleteOverlay(
visible: Boolean,
modifier: Modifier = Modifier,
) {
AnimatedVisibility(
visible = visible,
enter = fadeIn(),
exit = fadeOut(),
) {
Box(
modifier = modifier,
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.size(48.dp)
.background(Color.Black.copy(alpha = 0.5f), CircleShape),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = Icons.Rounded.Delete,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(24.dp)
)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.focusGroup
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues
Expand All @@ -14,20 +15,27 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.*
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.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import coil.compose.AsyncImage
import coil.request.ImageRequest
import com.lanlinju.animius.util.CROSSFADE_DURATION
import com.lanlinju.animius.util.focus.focusBorder
import com.lanlinju.animius.R as Res

/**
Expand All @@ -41,6 +49,7 @@ fun <T> MediaSmallRow(
content: @Composable (T) -> Unit
) {
LazyRow(
modifier = Modifier.focusGroup(),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(Res.dimen.small_padding)),
contentPadding = PaddingValues(
start = dimensionResource(Res.dimen.large_padding),
Expand All @@ -59,6 +68,7 @@ fun <T> MediaSmallRow(
* @param image A URL of the image to be shown in the card that this component is.
* @param label A label for the [image], if this is `null`, the [label] is not shown.
* @param onClick Action to happen when the card is clicked.
* @param onLongClick Action to happen when the card is long-pressed.
*/
@OptIn(ExperimentalFoundationApi::class)
@Composable
Expand All @@ -71,33 +81,33 @@ fun MediaSmall(
label: String? = null,
onSuccess: (Bitmap) -> Unit = { }
) {
MediaSmall(
val cardShape = RoundedCornerShape(dimensionResource(Res.dimen.media_card_corner_radius))

Card(
modifier = modifier
.clip(RoundedCornerShape(dimensionResource(Res.dimen.media_card_corner_radius)))
.focusBorder(
shape = cardShape,
width = 3.dp,
focusedColor = MaterialTheme.colorScheme.primary,
)
.clip(cardShape)
.combinedClickable(
enabled = enabled,
onClick = onClick,
onLongClick = onLongClick
),
image = image,
label = label,
onSuccess = onSuccess
)
}

@Composable
fun MediaSmall(
image: String?,
modifier: Modifier = Modifier,
label: String? = null,
onSuccess: (Bitmap) -> Unit = { }
) {
Card(
modifier = modifier,
elevation = CardDefaults.cardElevation(
defaultElevation = 0.dp,
pressedElevation = 0.dp,
focusedElevation = 0.dp,
hoveredElevation = 0.dp,
draggedElevation = 0.dp,
disabledElevation = 0.dp,
),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)
),
shape = RoundedCornerShape(dimensionResource(Res.dimen.media_card_corner_radius)),
shape = cardShape,
) {
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
Expand Down Expand Up @@ -160,4 +170,4 @@ fun PreviewMediaSmall() {
onClick = { },
modifier = Modifier.width(dimensionResource(Res.dimen.media_card_width))
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,19 @@ import androidx.compose.material.icons.rounded.Star
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.NavigationBarItemDefaults
import androidx.compose.material3.NavigationRail
import androidx.compose.material3.NavigationRailItem
import androidx.compose.material3.NavigationRailItemDefaults
import androidx.compose.material3.Text
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.onFocusChanged
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.dimensionResource
Expand All @@ -35,6 +41,7 @@ import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import java.util.Locale
import com.lanlinju.animius.R as Res
import com.lanlinju.animius.util.focus.rememberIsFocused

@Deprecated("Use AdaptiveNavigationBar instead")
@Composable
Expand Down Expand Up @@ -142,11 +149,20 @@ fun AdaptiveNavigationBar(
NavigationRail(modifier) {
destinations.forEachIndexed { index, destination ->
val selected = destination.route == currentDestination
val (isFocused, focusModifier) = rememberIsFocused()
NavigationRailItem(
selected = selected,
selected = selected || isFocused,
onClick = { onNavigateToDestination(index) },
icon = destination.icon,
label = { Text(destination.route) }, // 可选:添加标签
label = { Text(destination.route) },
colors = if (isFocused)
NavigationRailItemDefaults.colors(
selectedIconColor = MaterialTheme.colorScheme.onPrimary,
unselectedIconColor = MaterialTheme.colorScheme.onPrimary,
indicatorColor = MaterialTheme.colorScheme.primary
)
else NavigationRailItemDefaults.colors(),
modifier = focusModifier
)
}
}
Expand All @@ -155,11 +171,20 @@ fun AdaptiveNavigationBar(
NavigationBar(modifier) {
destinations.forEachIndexed { index, destination ->
val selected = destination.route == currentDestination
val (isFocused, focusModifier) = rememberIsFocused()
NavigationBarItem(
selected = selected,
selected = selected || isFocused,
onClick = { onNavigateToDestination(index) },
icon = destination.icon,
label = { Text(destination.label()) },
colors = if (isFocused)
NavigationBarItemDefaults.colors(
selectedIconColor = MaterialTheme.colorScheme.onPrimary,
unselectedIconColor = MaterialTheme.colorScheme.onPrimary,
indicatorColor = MaterialTheme.colorScheme.primary
)
else NavigationBarItemDefaults.colors(),
modifier = focusModifier
)
}
}
Expand Down
Loading